Implement IBL diffuse part
authorLuke Lau <luke_lau@icloud.com>
Sun, 18 Nov 2018 03:43:49 +0000 (03:43 +0000)
committerLuke Lau <luke_lau@icloud.com>
Sun, 18 Nov 2018 03:43:49 +0000 (03:43 +0000)
equirectangularvert.glsl [deleted file]
image.cpp
irradiancefrag.glsl [new file with mode: 0644]
main.cpp
pbrfrag.glsl
skybox.cpp
skybox.hpp
skyboxfrag.glsl
skyboxvert.glsl

diff --git a/equirectangularvert.glsl b/equirectangularvert.glsl
deleted file mode 100644 (file)
index dd21661..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#version 330
-
-in vec3 pos;
-out vec3 localPos;
-
-uniform mat4 projection;
-uniform mat4 view;
-
-void main() {
-       localPos = pos;
-       // flip image
-       localPos.y = -localPos.y;
-       vec4 fragPos = projection * view * vec4(pos, 1);
-       gl_Position = fragPos.xyww;
-}
index 13174c0..1565b85 100644 (file)
--- a/image.cpp
+++ b/image.cpp
@@ -6,18 +6,6 @@ Image::Image(const std::string &path) {
        CFURLRef url = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, false);
        CGImageSourceRef source = CGImageSourceCreateWithURL(url, NULL);
        CGImageRef ref = CGImageSourceCreateImageAtIndex(source, 0, NULL);
-       /* auto provider = CGDataProviderCreateWithFilename(path.c_str()); */
-       /* std::ifstream file(path); */
-       /* long magic; */
-       /* file.read((char*)&magic, 8); */
-       /* file.close(); */
-
-       /* CGImageRef ref; */
-
-       /* if (magic == 0x0a1a0a0d474e5089) // png magic number */
-       /*      ref = CGImageCreateWithPNGDataProvider(provider, nullptr, false, kCGRenderingIntentDefault); */
-       /* else */
-       /*      ref = CGImageCreateWithJPEGDataProvider(provider, nullptr, false, kCGRenderingIntentDefault); */
 
        _width = CGImageGetWidth(ref), _height = CGImageGetHeight(ref);
        info = CGImageGetBitmapInfo(ref);
@@ -36,6 +24,7 @@ unsigned char *Image::data() const {
 GLfloat Image::width() const { return _width; }
 GLfloat Image::height() const { return _height; }
 
+// TODO: Properly implement this for both internal format + format
 GLuint Image::format() const {
        if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelMonochrome) {
                return GL_DEPTH_COMPONENT;
diff --git a/irradiancefrag.glsl b/irradiancefrag.glsl
new file mode 100644 (file)
index 0000000..a1cc027
--- /dev/null
@@ -0,0 +1,34 @@
+#version 330
+
+in vec3 localPos;
+out vec4 fragColor;
+
+uniform samplerCube environmentMap;
+
+const float PI = 3.14159265359;
+
+void main() {
+       // sample dir = hemisphere's orientation
+       vec3 normal = normalize(localPos);
+       vec3 irradiance = vec3(0);
+
+       vec3 up = vec3(0, 1, 0);
+       vec3 right = cross(up, normal);
+       up = cross(normal, right);
+
+       const float sampleDelta = 0.025;
+       float numSamples = 0;
+       for (float phi = 0; phi < 2 * PI; phi += sampleDelta) {
+               for (float theta = 0; theta < 0.5 * PI; theta += sampleDelta) {
+                       vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
+                       // tangent space -> world space
+                       vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal;
+                       irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
+                       numSamples++;
+               }
+       }
+
+       irradiance = (PI * irradiance) / numSamples;
+
+       fragColor = vec4(irradiance, 1);
+}
index caaa2a3..8fbff62 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -161,10 +161,14 @@ void display() {
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, roughnessMap);
        
-       glUniform1i(glGetUniformLocation(pbrProg->progId, "aoMap"), 3);
+       glUniform1i(glGetUniformLocation(pbrProg->progId, "aoMap"), 4);
        glActiveTexture(GL_TEXTURE4);
        glBindTexture(GL_TEXTURE_2D, aoMap);
 
+       glUniform1i(glGetUniformLocation(pbrProg->progId, "irradianceMap"), 5);
+       glActiveTexture(GL_TEXTURE5);
+       glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->getIrradianceMap());
+
        pbr->draw();
 
        for (Light &light: lights) drawLight(light);
index 4421b99..16a9fa7 100644 (file)
@@ -12,6 +12,7 @@ uniform sampler2D normalMap;
 uniform sampler2D metallicMap;
 uniform sampler2D roughnessMap;
 uniform sampler2D aoMap;
+uniform samplerCube irradianceMap;
 
 out vec4 fragColor;
 
@@ -24,6 +25,10 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0) {
        return F0 + (1.f - F0) * pow(1.f - cosTheta, 5.f);
 }
 
+vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) {
+       return F0 + (max(vec3(1.f - roughness), F0) - F0) * pow(1.f - cosTheta, 5.f);
+}
+
 float distributionGGX(vec3 N, vec3 H, float roughness) {
        float a = roughness * roughness;
        float NdotH = mdot(N, H);
@@ -97,7 +102,9 @@ void main() {
                Lo += (kD * albedo / PI + specular) * radiance * mdot(N, L);
        }
 
-       vec3 ambient = vec3(0.03) * albedo * ao;
+       vec3 kD = 1.f - fresnelSchlickRoughness(mdot(N, V), F0, roughness);
+       vec3 diffuse = texture(irradianceMap, N).rgb * albedo;
+       vec3 ambient = (kD * diffuse) * ao;
        vec3 color = ambient + Lo;
 
        color = color / (color + vec3(1.f)); // map to HDR
index 45ab4ea..ab492ca 100644 (file)
@@ -2,13 +2,17 @@
 #include "skybox.hpp"
 #include <glm/gtc/type_ptr.hpp>
 
+GLuint setupCubeVertices(GLuint progId, bool reverse = false);
+
 Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
-       Program equiProg("equirectangularvert.glsl", "equirectangularfrag.glsl");
+       Program equiProg("skyboxvert.glsl", "equirectangularfrag.glsl");
        glUseProgram(equiProg.progId);
 
-       GLuint equiTexId;
-       glGenTextures(1, &equiTexId);
-       glBindTexture(GL_TEXTURE_2D, equiTexId);
+       GLuint allTexIds[3];
+       glGenTextures(3, allTexIds);
+       hdrTexId = allTexIds[0], cubemapTexId = allTexIds[1], irradianceTexId = allTexIds[2];
+       
+       glBindTexture(GL_TEXTURE_2D, hdrTexId);
 
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_SHORT, (unsigned short*)img.data());
 
@@ -27,9 +31,8 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureFBO);
 
-       // generate and setup cubemap texture
-       glGenTextures(1, &texId);
-       glBindTexture(GL_TEXTURE_CUBE_MAP, texId);
+       // setup cubemap texture
+       glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
        for (GLuint i = 0; i < 6; i++)
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -38,21 +41,8 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
-       // generate cube to render
-       glGenVertexArrays(1, &vao);
-       glBindVertexArray(vao);
-
-       GLuint vbo;
-       glGenBuffers(1, &vbo);
-
-       auto vertices = cube();
-
-       glBindBuffer(GL_ARRAY_BUFFER, vbo);
-       glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
-       
-       GLuint posLoc = glGetAttribLocation(program.progId, "pos");
-       glEnableVertexAttribArray(posLoc);
-       glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+       // generate vertices
+       setupCubeVertices(equiProg.progId);
 
        // render the cube
        glm::mat4 captureProj = glm::perspective(glm::radians(90.f), 1.f, 0.1f, 10.f);
@@ -68,36 +58,58 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
        glUniform1i(glGetUniformLocation(equiProg.progId, "equirectangularMap"), 0);
        glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
        glActiveTexture(GL_TEXTURE0);
-       glBindTexture(GL_TEXTURE_2D, equiTexId);
+       glBindTexture(GL_TEXTURE_2D, hdrTexId);
 
        glViewport(0, 0, 512, 512);
        glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
        for (GLuint i = 0; i < 6; i++) {
                glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
-               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, texId, 0);
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemapTexId, 0);
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                glDrawArrays(GL_TRIANGLES, 0, 36);
        }
 
+       // setup irradiance map texture
+       glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceTexId);
+       for (GLuint i = 0; i < 6; i++)
+               glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, GL_RGB, GL_FLOAT, nullptr);
+       glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
-       // switch back to regular skybox shader
-       glUseProgram(program.progId);
-       glDepthFunc(GL_LEQUAL);
+       // bind framebuffers for rendering irradiance map into
+       glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+       glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
+       glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32);
        
-       glGenVertexArrays(1, &vao);
-       glBindVertexArray(vao);
+       Program irradianceProg("skyboxvert.glsl", "irradiancefrag.glsl");
+       glUseProgram(irradianceProg.progId);
+       glUniform1i(glGetUniformLocation(irradianceProg.progId, "environmentMap"), 0);
+       glUniformMatrix4fv(glGetUniformLocation(irradianceProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
+       glActiveTexture(GL_TEXTURE0);
+       glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
 
-       glGenBuffers(1, &vbo);
+       // generate vertices
+       setupCubeVertices(irradianceProg.progId);
 
-       // reverse so facing inside out
-       std::reverse(vertices.begin(), vertices.end());
+       // render irradiance map
+       glViewport(0, 0, 32, 32);
+       glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+       for (GLuint i = 0; i < 6; i++) {
+               glUniformMatrix4fv(glGetUniformLocation(irradianceProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
+               glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceTexId, 0);
+               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+               glDrawArrays(GL_TRIANGLES, 0, 36);
+       }
 
-       glBindBuffer(GL_ARRAY_BUFFER, vbo);
-       glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
+       // switch back to regular skybox shader
+       glUseProgram(program.progId);
+       glDepthFunc(GL_LEQUAL);
 
-       posLoc = glGetAttribLocation(program.progId, "pos");
-       glEnableVertexAttribArray(posLoc);
-       glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+       // reverse so facing inside out
+       vao = setupCubeVertices(program.progId, true);
 
        // restore default framebuffer
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -116,12 +128,31 @@ void Skybox::draw(glm::mat4 proj, glm::mat4 view) const {
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
 
        glActiveTexture(GL_TEXTURE0);
-       glBindTexture(GL_TEXTURE_CUBE_MAP, texId);
+       glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceTexId);
        glDrawArrays(GL_TRIANGLES, 0, 36);
 
        if (glGetError()) exit(1);
 }
 
-GLuint Skybox::getTexture() const {
-       return texId;
+GLuint setupCubeVertices(GLuint progId, bool reverse) {
+       GLuint vao;
+       glGenVertexArrays(1, &vao);
+       glBindVertexArray(vao);
+
+       GLuint vbo;
+       glGenBuffers(1, &vbo);
+
+       auto vertices = cube();
+
+       if (reverse)
+               std::reverse(vertices.begin(), vertices.end());
+
+       glBindBuffer(GL_ARRAY_BUFFER, vbo);
+       glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
+       
+       GLuint posLoc = glGetAttribLocation(progId, "pos");
+       glEnableVertexAttribArray(posLoc);
+       glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+       
+       return vao;
 }
index 0b52375..40ed1a1 100644 (file)
@@ -12,9 +12,11 @@ class Skybox {
                // img must be HDR
                Skybox(const Image img);
                void draw(glm::mat4 proj, glm::mat4 view) const;
-               GLuint getTexture() const;
+               GLuint getTexture() const { return cubemapTexId; }
+               GLuint getIrradianceMap() const { return irradianceTexId; }
        private:
-               GLuint texId, vao;
+               GLuint hdrTexId, cubemapTexId, irradianceTexId;
+               GLuint vao;
                const Program program;
 };
 
index 9dc04bf..81bf58b 100644 (file)
@@ -1,12 +1,12 @@
 #version 330
 
-in vec3 texCoords;
+in vec3 localPos;
 out vec4 fragColor;
 
 uniform samplerCube skybox;
 
 void main() {
-       vec3 env = texture(skybox, texCoords).rgb;
+       vec3 env = texture(skybox, localPos).rgb;
        env = env / (env + vec3(1.f));
        env = pow(env, vec3(1.f / 2.2));
        fragColor = vec4(env, 1.f);
index 4946c73..8b75395 100644 (file)
@@ -1,13 +1,13 @@
 #version 330
 
 in vec3 pos;
-out vec3 texCoords;
+out vec3 localPos;
 
 uniform mat4 projection;
 uniform mat4 view;
 
 void main() {
-       texCoords = pos;
+       localPos = pos;
        vec4 fragPos = projection * view * vec4(pos, 1);
        gl_Position = fragPos.xyww;
 }