From: Luke Lau Date: Sun, 18 Nov 2018 03:43:49 +0000 (+0000) Subject: Implement IBL diffuse part X-Git-Tag: cs7gv3-a3~17 X-Git-Url: https://git.lukelau.me/?p=opengl.git;a=commitdiff_plain;h=9e43c799021b7bcca324b988aae44e98b05d10b4 Implement IBL diffuse part --- diff --git a/equirectangularvert.glsl b/equirectangularvert.glsl deleted file mode 100644 index dd21661..0000000 --- a/equirectangularvert.glsl +++ /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; -} diff --git a/image.cpp b/image.cpp index 13174c0..1565b85 100644 --- 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 index 0000000..a1cc027 --- /dev/null +++ b/irradiancefrag.glsl @@ -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); +} diff --git a/main.cpp b/main.cpp index caaa2a3..8fbff62 100644 --- 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); diff --git a/pbrfrag.glsl b/pbrfrag.glsl index 4421b99..16a9fa7 100644 --- a/pbrfrag.glsl +++ b/pbrfrag.glsl @@ -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 diff --git a/skybox.cpp b/skybox.cpp index 45ab4ea..ab492ca 100644 --- a/skybox.cpp +++ b/skybox.cpp @@ -2,13 +2,17 @@ #include "skybox.hpp" #include +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; } diff --git a/skybox.hpp b/skybox.hpp index 0b52375..40ed1a1 100644 --- a/skybox.hpp +++ b/skybox.hpp @@ -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; }; diff --git a/skyboxfrag.glsl b/skyboxfrag.glsl index 9dc04bf..81bf58b 100644 --- a/skyboxfrag.glsl +++ b/skyboxfrag.glsl @@ -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); diff --git a/skyboxvert.glsl b/skyboxvert.glsl index 4946c73..8b75395 100644 --- a/skyboxvert.glsl +++ b/skyboxvert.glsl @@ -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; }