Add specular component
authorLuke Lau <luke_lau@icloud.com>
Mon, 19 Nov 2018 23:00:47 +0000 (23:00 +0000)
committerLuke Lau <luke_lau@icloud.com>
Mon, 19 Nov 2018 23:00:47 +0000 (23:00 +0000)
Also add hi-res GLUT display

Makefile
brdffrag.glsl [new file with mode: 0644]
brdfvert.glsl [new file with mode: 0644]
cocoa.h [new file with mode: 0644]
cocoa.mm [new file with mode: 0644]
main.cpp
pbrfrag.glsl
prefilterfrag.glsl [new file with mode: 0644]
shapes.hpp
skybox.cpp
skybox.hpp

index 9ee7707..208ae4f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 all: main
 
 main:
-       clang++ -g --std=c++17 *.cpp -L/usr/local/lib -lassimp -framework OpenGL -framework glut -framework CoreGraphics -framework CoreFoundation -framework ImageIO -lglew -o bin/main
+       clang++ -g --std=c++17 *.cpp *.mm -L/usr/local/lib -lassimp -framework OpenGL -framework glut -framework CoreGraphics -framework CoreFoundation -framework Cocoa -framework ImageIO -lglew -o bin/main
        ctags *.cpp
diff --git a/brdffrag.glsl b/brdffrag.glsl
new file mode 100644 (file)
index 0000000..93403de
--- /dev/null
@@ -0,0 +1,90 @@
+#version 330
+
+in vec2 texCoords;
+out vec4 fragColor;
+
+const float PI = 3.14159265359;
+
+//TODO: Put this in a separate shader program
+float radicalInverse(uint bits) {
+       bits = (bits << 16u) | (bits >> 16u);
+       bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+    bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+    bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+    bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+    return float(bits) * 2.3283064365386963e-10;
+}
+
+vec2 hammersley(uint i, uint N) {
+       return vec2(float(i) / float(N), radicalInverse(i));
+}
+
+float geometrySchlickGGX(float NdotV, float roughness) {
+       float a = roughness * roughness;
+       float k = (a * a) / 2.f;
+       
+       return NdotV / (NdotV * (1.f - k) + k);
+}
+
+float geometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
+       float ggx1 = geometrySchlickGGX(max(dot(N, L), 0.f), roughness);
+       float ggx2 = geometrySchlickGGX(max(dot(N, V), 0.f), roughness);
+       return ggx1 * ggx2;
+}
+
+vec3 importanceSampleGGX(vec2 Xi, vec3 N, float roughness) {
+       float a = roughness * roughness;
+
+       float phi = 2.f * PI * Xi.x;
+       float cosTheta = sqrt((1.f - Xi.y) / (1.f + (a * a - 1.f) * Xi.y));
+       float sinTheta = sqrt(1.f - cosTheta * cosTheta);
+
+       // spherical -> cartesian
+       vec3 H = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
+
+       vec3 up = abs(N.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
+       vec3 tangent = normalize(cross(up, N));
+       vec3 bitangent = cross(N, tangent);
+
+       vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+       return normalize(sampleVec);
+}
+
+vec2 integrateBRDF(float NdotV, float roughness) {
+       vec3 V = vec3(sqrt(1.f - NdotV * NdotV), 0.f, NdotV);
+
+       float A = 0, B = 0;
+
+       vec3 N = vec3(0, 0, 1);
+
+       const uint sampleCount = 1024u;
+       for (uint i = 0u; i < sampleCount; i++) {
+               vec2 Xi = hammersley(i, sampleCount);
+               vec3 H = importanceSampleGGX(Xi, N, roughness);
+               vec3 L = normalize(2.f * dot(V, H) * H - V);
+
+               float NdotL = max(L.z, 0.f);
+               float NdotH = max(H.z, 0.f);
+               float VdotH = max(dot(V, H), 0.f);
+
+               if (NdotL > 0) {
+                       float G = geometrySmith(N, V, L, roughness);
+                       float Gvis = (G * VdotH) / (NdotH * NdotV);
+                       float Fc = pow(1.f - VdotH, 5.f);
+
+                       A += (1.f - Fc) * Gvis;
+                       B += Fc * Gvis;
+               }
+       }
+
+       A /= float(sampleCount);
+       B /= float(sampleCount);
+
+       return vec2(A, B);
+}
+
+void main() {
+       fragColor = vec4(integrateBRDF(texCoords.x, texCoords.y), 0, 0);
+}
+
+       
diff --git a/brdfvert.glsl b/brdfvert.glsl
new file mode 100644 (file)
index 0000000..11d63ef
--- /dev/null
@@ -0,0 +1,9 @@
+#version 330
+
+in vec2 pos;
+out vec2 texCoords;
+
+void main() {
+       texCoords = (pos + 1) / 2;
+       gl_Position = vec4(pos, 0, 1);
+}
diff --git a/cocoa.h b/cocoa.h
new file mode 100644 (file)
index 0000000..5dcf66d
--- /dev/null
+++ b/cocoa.h
@@ -0,0 +1 @@
+void swizzle();
diff --git a/cocoa.mm b/cocoa.mm
new file mode 100644 (file)
index 0000000..ce2549b
--- /dev/null
+++ b/cocoa.mm
@@ -0,0 +1,6 @@
+#import <Cocoa/Cocoa.h>
+void swizzle() {
+       NSWindow *window = [NSApp orderedWindows][0];
+       NSView *view = [window contentView];
+       [view setWantsBestResolutionOpenGLSurface: true];
+}
index 8fbff62..9464e9e 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -5,6 +5,7 @@
 #include <vector>
 #ifdef __APPLE__
 #include <GL/glew.h>
+#include "cocoa.h"
 #else
 #include <OpenGL/glew.h>
 #endif
@@ -169,6 +170,14 @@ void display() {
        glActiveTexture(GL_TEXTURE5);
        glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->getIrradianceMap());
 
+       glUniform1i(glGetUniformLocation(pbrProg->progId, "prefilterMap"), 6);
+       glActiveTexture(GL_TEXTURE6);
+       glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->getPrefilterMap());
+
+       glUniform1i(glGetUniformLocation(pbrProg->progId, "brdfMap"), 7);
+       glActiveTexture(GL_TEXTURE7);
+       glBindTexture(GL_TEXTURE_2D, skybox->getBRDFMap());
+
        pbr->draw();
 
        for (Light &light: lights) drawLight(light);
@@ -220,14 +229,16 @@ void init() {
        glUseProgram(pbrProg->progId);
        pbr = new Model("models/sphere.dae", *pbrProg, *skybox);
 
-       albedoMap = loadTexture("models/darktiles/darktiles1_basecolor.png");
-       metallicMap = loadTexture("models/darktiles/darktiles1_metallic.png");
-       normalMap = loadTexture("models/darktiles/darktiles1_normal.png");
-       roughnessMap = loadTexture("models/darktiles/darktiles1_roughness.png");
-       aoMap = loadTexture("models/darktiles/darktiles1_AO.png");
+       albedoMap = loadTexture("models/streakedmetal/streakedmetal_albedo.png");
+       metallicMap = loadTexture("models/streakedmetal/streakedmetal_metalness.png");
+       normalMap = loadTexture("models/streakedmetal/streakedmetal_normal.png");
+       roughnessMap = loadTexture("models/streakedmetal/streakedmetal_roughness.png");
+       aoMap = loadTexture("models/streakedmetal/streakedmetal_ao.png");
 
        glEnable(GL_DEPTH_TEST); 
        glEnable(GL_CULL_FACE); 
+       // prevent edge artifacts in specular cubemaps
+       glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
 }
 
 bool* keyStates = new bool[256];
@@ -322,6 +333,8 @@ int main(int argc, char** argv) {
        
        init();
        
+       swizzle();
+
        glutKeyboardFunc(keyboard);
        glutKeyboardUpFunc(keyboardUp);
        glutTimerFunc(16, timer, 0);
index 16a9fa7..99523ec 100644 (file)
@@ -13,6 +13,8 @@ uniform sampler2D metallicMap;
 uniform sampler2D roughnessMap;
 uniform sampler2D aoMap;
 uniform samplerCube irradianceMap;
+uniform samplerCube prefilterMap;
+uniform sampler2D brdfMap;
 
 out vec4 fragColor;
 
@@ -76,6 +78,8 @@ void main() {
 
        vec3 F0 = mix(vec3(0.04), albedo, metallic);
 
+       vec3 R = reflect(-V, N);
+
        // reflectance
        vec3 Lo = vec3(0.f);
        for (int i = 0; i < lightPositions.length(); i++) {
@@ -102,9 +106,20 @@ void main() {
                Lo += (kD * albedo / PI + specular) * radiance * mdot(N, L);
        }
 
-       vec3 kD = 1.f - fresnelSchlickRoughness(mdot(N, V), F0, roughness);
-       vec3 diffuse = texture(irradianceMap, N).rgb * albedo;
-       vec3 ambient = (kD * diffuse) * ao;
+       vec3 F = fresnelSchlickRoughness(mdot(N, V), F0, roughness);
+       
+       vec3 kS = F;
+       vec3 kD = (1.f - kS) * (1.f - metallic);
+
+       vec3 irradiance = texture(irradianceMap, N).rgb;
+       vec3 diffuse = irradiance * albedo;
+
+       const float maxReflectionLoD = 4.f;
+       vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * maxReflectionLoD).rgb;
+       vec2 envBRDF = texture(brdfMap, vec2(mdot(N, V), roughness)).rg;
+       vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y);
+
+       vec3 ambient = (kD * diffuse + specular) * ao;
        vec3 color = ambient + Lo;
 
        color = color / (color + vec3(1.f)); // map to HDR
diff --git a/prefilterfrag.glsl b/prefilterfrag.glsl
new file mode 100644 (file)
index 0000000..87cd70e
--- /dev/null
@@ -0,0 +1,67 @@
+#version 330
+
+in vec3 localPos, normal;
+out vec4 fragColor;
+
+uniform samplerCube environmentMap;
+uniform float roughness;
+
+const float PI = 3.14159265359;
+
+//TODO: Put this in a separate shader program
+float radicalInverse(uint bits) {
+       bits = (bits << 16u) | (bits >> 16u);
+       bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+    bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+    bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+    bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+    return float(bits) * 2.3283064365386963e-10;
+}
+
+vec2 hammersley(uint i, uint N) {
+       return vec2(float(i) / float(N), radicalInverse(i));
+}
+
+vec3 importanceSampleGGX(vec2 Xi, vec3 N, float roughness) {
+       float a = roughness * roughness;
+
+       float phi = 2.f * PI * Xi.x;
+       float cosTheta = sqrt((1.f - Xi.y) / (1.f + (a * a - 1.f) * Xi.y));
+       float sinTheta = sqrt(1.f - cosTheta * cosTheta);
+
+       // spherical -> cartesian
+       vec3 H = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
+
+       vec3 up = abs(N.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
+       vec3 tangent = normalize(cross(up, N));
+       vec3 bitangent = cross(N, tangent);
+
+       vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
+       return normalize(sampleVec);
+}
+
+void main() {
+       vec3 N = normalize(localPos);
+
+       // approximate view direction - no grazing specular reflections
+       vec3 R = N;
+       vec3 V = R;
+
+       const uint sampleCount = 1024u;
+       float totalWeight = 0;
+       vec3 prefilteredColor = vec3(0);
+       for (uint i = 0u; i < sampleCount; i++) {
+               vec2 Xi = hammersley(i, sampleCount);
+               vec3 H = importanceSampleGGX(Xi, N, roughness);
+               vec3 L = normalize(2.f * dot(V, H) * H - V);
+
+               float NdotL = max(dot(N, L), 0.f);
+               if (NdotL > 0) {
+                       prefilteredColor += texture(environmentMap, L).rgb * NdotL;
+                       totalWeight += NdotL;
+               }
+       }
+
+       prefilteredColor = prefilteredColor / totalWeight;
+       fragColor = vec4(prefilteredColor, 1.f);
+}
index 5fcefdb..63263b5 100644 (file)
@@ -15,6 +15,15 @@ constexpr array<glm::vec3, 6> quadToTriangles(const array<glm::vec3, 4> quads) {
        };
 }
 
+constexpr array<glm::vec3, 6> plane() {
+       return quadToTriangles({
+               glm::vec3(1, 1, 0),
+               glm::vec3(-1, 1, 0),
+               glm::vec3(-1, -1, 0),
+               glm::vec3(1, -1, 0)
+       });
+}
+
 constexpr array<glm::vec3, 36> cube() {
        int i = 0;
        array<glm::vec3, 36> vertices;
index ab492ca..cb79fb3 100644 (file)
@@ -2,15 +2,96 @@
 #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);
 
@@ -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 <std::size_t N>
+GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> 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());
 
index 40ed1a1..6961e8d 100644 (file)
@@ -14,10 +14,15 @@ class Skybox {
                void draw(glm::mat4 proj, glm::mat4 view) const;
                GLuint getTexture() const { return cubemapTexId; }
                GLuint getIrradianceMap() const { return irradianceTexId; }
+               GLuint getPrefilterMap() const { return prefilterTexId; }
+               GLuint getBRDFMap() const { return brdfMapTexId; }
        private:
-               GLuint hdrTexId, cubemapTexId, irradianceTexId;
+               GLuint hdrTexId, cubemapTexId, irradianceTexId, prefilterTexId, brdfMapTexId;
+               GLuint captureFBO, captureRBO;
                GLuint vao;
                const Program program;
+               void generatePrefilterMap() const;
+               void generateBRDFMap() const;
 };
 
 #endif