Implement IBL diffuse part
[opengl.git] / skybox.cpp
1 #include "shapes.hpp"
2 #include "skybox.hpp"
3 #include <glm/gtc/type_ptr.hpp>
4
5 GLuint setupCubeVertices(GLuint progId, bool reverse = false);
6
7 Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
8         Program equiProg("skyboxvert.glsl", "equirectangularfrag.glsl");
9         glUseProgram(equiProg.progId);
10
11         GLuint allTexIds[3];
12         glGenTextures(3, allTexIds);
13         hdrTexId = allTexIds[0], cubemapTexId = allTexIds[1], irradianceTexId = allTexIds[2];
14         
15         glBindTexture(GL_TEXTURE_2D, hdrTexId);
16
17         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_SHORT, (unsigned short*)img.data());
18
19         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
20         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
21         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
22         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
23
24         // generate framebuffers to store cubemap in
25         GLuint captureFBO, captureRBO;
26         glGenFramebuffers(1, &captureFBO);
27         glGenRenderbuffers(1, &captureRBO);
28
29         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
30         glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
31         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
32         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureFBO);
33
34         // setup cubemap texture
35         glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
36         for (GLuint i = 0; i < 6; i++)
37                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr);
38         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
39         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
40         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
41         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
42         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
43
44         // generate vertices
45         setupCubeVertices(equiProg.progId);
46
47         // render the cube
48         glm::mat4 captureProj = glm::perspective(glm::radians(90.f), 1.f, 0.1f, 10.f);
49         glm::mat4 captureViews[] = {
50                 glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 1,  0,  0), glm::vec3(0, -1,  0)),
51                 glm::lookAt(glm::vec3(0, 0, 0), glm::vec3(-1,  0,  0), glm::vec3(0, -1,  0)),
52                 glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0,  1,  0), glm::vec3(0,  0,  1)),
53                 glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, -1,  0), glm::vec3(0,  0, -1)),
54                 glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0,  0,  1), glm::vec3(0, -1,  0)),
55                 glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0,  0, -1), glm::vec3(0, -1,  0))
56         };
57
58         glUniform1i(glGetUniformLocation(equiProg.progId, "equirectangularMap"), 0);
59         glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
60         glActiveTexture(GL_TEXTURE0);
61         glBindTexture(GL_TEXTURE_2D, hdrTexId);
62
63         glViewport(0, 0, 512, 512);
64         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
65         for (GLuint i = 0; i < 6; i++) {
66                 glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
67                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemapTexId, 0);
68                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
69                 glDrawArrays(GL_TRIANGLES, 0, 36);
70         }
71
72         // setup irradiance map texture
73         glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceTexId);
74         for (GLuint i = 0; i < 6; i++)
75                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, GL_RGB, GL_FLOAT, nullptr);
76         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
77         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
78         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
79         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
80         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
81
82         // bind framebuffers for rendering irradiance map into
83         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
84         glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
85         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32);
86         
87         Program irradianceProg("skyboxvert.glsl", "irradiancefrag.glsl");
88         glUseProgram(irradianceProg.progId);
89         glUniform1i(glGetUniformLocation(irradianceProg.progId, "environmentMap"), 0);
90         glUniformMatrix4fv(glGetUniformLocation(irradianceProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
91         glActiveTexture(GL_TEXTURE0);
92         glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
93
94         // generate vertices
95         setupCubeVertices(irradianceProg.progId);
96
97         // render irradiance map
98         glViewport(0, 0, 32, 32);
99         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
100         for (GLuint i = 0; i < 6; i++) {
101                 glUniformMatrix4fv(glGetUniformLocation(irradianceProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
102                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceTexId, 0);
103                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
104                 glDrawArrays(GL_TRIANGLES, 0, 36);
105         }
106
107         // switch back to regular skybox shader
108         glUseProgram(program.progId);
109         glDepthFunc(GL_LEQUAL);
110
111         // reverse so facing inside out
112         vao = setupCubeVertices(program.progId, true);
113
114         // restore default framebuffer
115         glBindFramebuffer(GL_FRAMEBUFFER, 0);
116 }
117
118 void Skybox::draw(glm::mat4 proj, glm::mat4 view) const {
119         glUseProgram(program.progId);
120
121         glBindVertexArray(vao);
122
123         GLuint projLoc = glGetUniformLocation(program.progId, "projection");
124         glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
125
126         GLuint viewLoc = glGetUniformLocation(program.progId, "view");
127         view = glm::mat4(glm::mat3(view));
128         glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
129
130         glActiveTexture(GL_TEXTURE0);
131         glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceTexId);
132         glDrawArrays(GL_TRIANGLES, 0, 36);
133
134         if (glGetError()) exit(1);
135 }
136
137 GLuint setupCubeVertices(GLuint progId, bool reverse) {
138         GLuint vao;
139         glGenVertexArrays(1, &vao);
140         glBindVertexArray(vao);
141
142         GLuint vbo;
143         glGenBuffers(1, &vbo);
144
145         auto vertices = cube();
146
147         if (reverse)
148                 std::reverse(vertices.begin(), vertices.end());
149
150         glBindBuffer(GL_ARRAY_BUFFER, vbo);
151         glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
152         
153         GLuint posLoc = glGetAttribLocation(progId, "pos");
154         glEnableVertexAttribArray(posLoc);
155         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
156         
157         return vao;
158 }