#include "skybox.hpp"
#include <glm/gtc/type_ptr.hpp>
-GLuint setupCubeVertices(GLuint progId, bool reverse = false);
+template <std::size_t N>
+GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> 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);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_SHORT, (unsigned short*)img.data());
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, img.width(), img.height(), 0, GL_RGB, GL_HALF_FLOAT, (uint16_t*)img.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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);
+ constexpr GLuint CUBEMAP_WIDTH = 1024, CUBEMAP_HEIGHT = 1024;
+
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, CUBEMAP_WIDTH, CUBEMAP_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureFBO);
// 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);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, CUBEMAP_WIDTH, CUBEMAP_HEIGHT, 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_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));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, hdrTexId);
- glViewport(0, 0, 512, 512);
+ glViewport(0, 0, CUBEMAP_WIDTH, CUBEMAP_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
for (GLuint i = 0; i < 6; i++) {
glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
// generate vertices
- setupCubeVertices(irradianceProg.progId);
+ setupVertices(irradianceProg.progId, cube());
// render irradiance map
glViewport(0, 0, 32, 32);
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);
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 <std::size_t N>
+GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> vertices, 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());