X-Git-Url: http://git.lukelau.me/?p=opengl.git;a=blobdiff_plain;f=skybox.cpp;h=cb79fb327f913c40858c77e09ecf996dfe1a7ea1;hp=ab492ca9a330129a660421999ef7a330fe2c360b;hb=d80972d96e5fcd444657f937ab2700039efa83d2;hpb=9e43c799021b7bcca324b988aae44e98b05d10b4 diff --git a/skybox.cpp b/skybox.cpp index ab492ca..cb79fb3 100644 --- a/skybox.cpp +++ b/skybox.cpp @@ -2,15 +2,96 @@ #include "skybox.hpp" #include -GLuint setupCubeVertices(GLuint progId, bool reverse = false); +template +GLuint setupVertices(GLuint progId, std::array vertices, bool reverse = false); + +// matrices used when capturing various environment maps +glm::mat4 captureProj = glm::perspective(glm::radians(90.f), 1.f, 0.1f, 10.f); +glm::mat4 captureViews[] = { + glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 1, 0, 0), glm::vec3(0, -1, 0)), + glm::lookAt(glm::vec3(0, 0, 0), glm::vec3(-1, 0, 0), glm::vec3(0, -1, 0)), + glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, 1, 0), glm::vec3(0, 0, 1)), + glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, -1, 0), glm::vec3(0, 0, -1)), + glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, 0, 1), glm::vec3(0, -1, 0)), + glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, 0, -1), glm::vec3(0, -1, 0)) +}; + +void Skybox::generatePrefilterMap() const { + glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterTexId); + for (GLuint i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 , GL_RGB16F, 128, 128, 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_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + Program prefilterProg("skyboxvert.glsl", "prefilterfrag.glsl"); + glUseProgram(prefilterProg.progId); + glUniform1i(glGetUniformLocation(prefilterProg.progId, "environmentMap"), 0); + glUniformMatrix4fv(glGetUniformLocation(prefilterProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj)); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId); + + setupVertices(prefilterProg.progId, cube()); + + glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + constexpr GLuint MAX_MIP_LEVELS = 5; + for (GLuint mip = 0; mip < MAX_MIP_LEVELS; mip++) { + GLuint mipWidth = 128 * std::pow(0.5, mip); + GLuint mipHeight = 128 * std::pow(0.5, mip); + glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); + glViewport(0, 0, mipWidth, mipHeight); + + float roughness = (float)mip / (MAX_MIP_LEVELS - 1.f); + glUniform1f(glGetUniformLocation(prefilterProg.progId, "roughness"), roughness); + + for (GLuint i = 0; i < 6; i++) { + glUniformMatrix4fv(glGetUniformLocation(prefilterProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i])); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilterTexId, mip); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 36); + } + } +} + +void Skybox::generateBRDFMap() const { + glBindTexture(GL_TEXTURE_2D, brdfMapTexId); + // allocate memory + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, 512, 512, 0, GL_RG, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); + glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdfMapTexId, 0); + + glViewport(0, 0, 512, 512); + Program prog("brdfvert.glsl", "brdffrag.glsl"); + glUseProgram(prog.progId); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + setupVertices(prog.progId, plane()); + glDrawArrays(GL_TRIANGLES, 0, 6); +} Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") { Program equiProg("skyboxvert.glsl", "equirectangularfrag.glsl"); glUseProgram(equiProg.progId); - GLuint allTexIds[3]; - glGenTextures(3, allTexIds); - hdrTexId = allTexIds[0], cubemapTexId = allTexIds[1], irradianceTexId = allTexIds[2]; + GLuint allTexIds[5]; + glGenTextures(5, allTexIds); + hdrTexId = allTexIds[0]; + cubemapTexId = allTexIds[1]; + irradianceTexId = allTexIds[2]; + prefilterTexId = allTexIds[3]; + brdfMapTexId = allTexIds[4]; glBindTexture(GL_TEXTURE_2D, hdrTexId); @@ -22,7 +103,6 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // generate framebuffers to store cubemap in - GLuint captureFBO, captureRBO; glGenFramebuffers(1, &captureFBO); glGenRenderbuffers(1, &captureRBO); @@ -42,18 +122,9 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // generate vertices - setupCubeVertices(equiProg.progId); + setupVertices(equiProg.progId, cube()); // render the cube - glm::mat4 captureProj = glm::perspective(glm::radians(90.f), 1.f, 0.1f, 10.f); - glm::mat4 captureViews[] = { - glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 1, 0, 0), glm::vec3(0, -1, 0)), - glm::lookAt(glm::vec3(0, 0, 0), glm::vec3(-1, 0, 0), glm::vec3(0, -1, 0)), - glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, 1, 0), glm::vec3(0, 0, 1)), - glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, -1, 0), glm::vec3(0, 0, -1)), - glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, 0, 1), glm::vec3(0, -1, 0)), - glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, 0, -1), glm::vec3(0, -1, 0)) - }; glUniform1i(glGetUniformLocation(equiProg.progId, "equirectangularMap"), 0); glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj)); @@ -92,7 +163,7 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") { glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId); // generate vertices - setupCubeVertices(irradianceProg.progId); + setupVertices(irradianceProg.progId, cube()); // render irradiance map glViewport(0, 0, 32, 32); @@ -104,12 +175,16 @@ Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") { glDrawArrays(GL_TRIANGLES, 0, 36); } + generatePrefilterMap(); + + generateBRDFMap(); + // switch back to regular skybox shader glUseProgram(program.progId); glDepthFunc(GL_LEQUAL); // reverse so facing inside out - vao = setupCubeVertices(program.progId, true); + vao = setupVertices(program.progId, cube(), true); // restore default framebuffer glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -128,13 +203,14 @@ 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, irradianceTexId); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId); glDrawArrays(GL_TRIANGLES, 0, 36); if (glGetError()) exit(1); } -GLuint setupCubeVertices(GLuint progId, bool reverse) { +template +GLuint setupVertices(GLuint progId, std::array vertices, bool reverse) { GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); @@ -142,8 +218,6 @@ GLuint setupCubeVertices(GLuint progId, bool reverse) { GLuint vbo; glGenBuffers(1, &vbo); - auto vertices = cube(); - if (reverse) std::reverse(vertices.begin(), vertices.end());