#include "shapes.hpp"
#include "skybox.hpp"
-#include "image.hpp"
#include <glm/gtc/type_ptr.hpp>
-Skybox::Skybox(const std::vector<std::string> faces): program("skyboxvert.glsl", "skyboxfrag.glsl") {
- glGenTextures(1, &texId);
- glBindTexture(GL_TEXTURE_CUBE_MAP, texId);
- glDepthFunc(GL_LEQUAL);
+GLuint setupCubeVertices(GLuint progId, bool reverse = false);
- int width, height, numChans;
- for (int i = 0; i < faces.size(); i++) {
- Image img(faces[i]);
+Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
+ Program equiProg("skyboxvert.glsl", "equirectangularfrag.glsl");
+ glUseProgram(equiProg.progId);
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data());
- }
+ 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());
+
+ 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_S, GL_CLAMP_TO_EDGE);
+ 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);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+ glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
+ 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);
+ 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);
+
+ // generate vertices
+ setupCubeVertices(equiProg.progId);
+
+ // 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);
+ 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, 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);
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
+ // bind framebuffers for rendering irradiance map into
+ glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+ glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32);
- GLuint vbo;
- glGenBuffers(1, &vbo);
+ 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);
+
+ // generate vertices
+ setupCubeVertices(irradianceProg.progId);
+
+ // 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);
+ }
- auto vertices = cube();
+ // switch back to regular skybox shader
+ glUseProgram(program.progId);
+ glDepthFunc(GL_LEQUAL);
// reverse so facing inside out
- std::reverse(vertices.begin(), vertices.end());
+ vao = setupCubeVertices(program.progId, true);
- 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);
+ // restore default framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
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;
}