Add a .clang-format
[opengl.git] / skybox.cpp
1 #include "shapes.hpp"
2 #include "skybox.hpp"
3 #include <glm/gtc/type_ptr.hpp>
4
5 template <std::size_t N>
6 GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> vertices, bool reverse = false);
7
8 // matrices used when capturing various environment maps
9 const glm::mat4 captureProj = glm::perspective(glm::radians(90.f), 1.f, 0.1f, 10.f);
10 const glm::mat4 captureViews[] = {
11         glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 1,  0,  0), glm::vec3(0, -1,  0)),
12         glm::lookAt(glm::vec3(0, 0, 0), glm::vec3(-1,  0,  0), glm::vec3(0, -1,  0)),
13         glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0,  1,  0), glm::vec3(0,  0,  1)),
14         glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0, -1,  0), glm::vec3(0,  0, -1)),
15         glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0,  0,  1), glm::vec3(0, -1,  0)),
16         glm::lookAt(glm::vec3(0, 0, 0), glm::vec3( 0,  0, -1), glm::vec3(0, -1,  0))
17 };
18
19 void Skybox::generatePrefilterMap() const {
20         glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterTexId);
21         for (GLuint i = 0; i < 6; i++)
22                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 , GL_RGB16F, 128, 128, 0, GL_RGB, GL_HALF_FLOAT, nullptr);
23         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
24         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
25         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
26         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
27         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
28
29         glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
30
31         Program prefilterProg("skyboxvert.glsl", "prefilterfrag.glsl");
32         glUseProgram(prefilterProg.progId);
33         glUniform1i(glGetUniformLocation(prefilterProg.progId, "environmentMap"), 0);
34         glUniformMatrix4fv(glGetUniformLocation(prefilterProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
35         glActiveTexture(GL_TEXTURE0);
36         glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
37         
38         setupVertices(prefilterProg.progId, cubeArray());
39         
40         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
41         constexpr GLuint MAX_MIP_LEVELS = 5;
42         for (GLuint mip = 0; mip < MAX_MIP_LEVELS; mip++) {
43                 GLuint mipWidth = 128 * std::pow(0.5, mip);
44                 GLuint mipHeight = 128 * std::pow(0.5, mip);
45                 glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
46                 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight);
47                 glViewport(0, 0, mipWidth, mipHeight);
48
49                 float roughness = (float)mip / (MAX_MIP_LEVELS - 1.f);
50                 glUniform1f(glGetUniformLocation(prefilterProg.progId, "roughness"), roughness);
51                 
52                 for (GLuint i = 0; i < 6; i++) {
53                         glUniformMatrix4fv(glGetUniformLocation(prefilterProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
54                         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilterTexId, mip);
55                         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
56                         glDrawArrays(GL_TRIANGLES, 0, 36);
57                 }
58         }
59 }
60
61 void Skybox::generateBRDFMap() const {
62         glBindTexture(GL_TEXTURE_2D, brdfMapTexId);
63         // allocate memory
64         glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, 512, 512, 0, GL_RG, GL_FLOAT, 0);
65         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
66         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
67         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
68         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
69
70         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
71         glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
72         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);
73         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdfMapTexId, 0);
74
75         glViewport(0, 0, 512, 512);
76         Program prog("brdfvert.glsl", "brdffrag.glsl");
77         glUseProgram(prog.progId);
78         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
79         
80         setupVertices(prog.progId, plane());
81         glDrawArrays(GL_TRIANGLES, 0, 6);
82 }
83
84 Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
85         Program equiProg("skyboxvert.glsl", "equirectangularfrag.glsl");
86         glUseProgram(equiProg.progId);
87
88         GLuint allTexIds[5];
89         glGenTextures(5, allTexIds);
90         hdrTexId = allTexIds[0];
91         cubemapTexId = allTexIds[1];
92         irradianceTexId = allTexIds[2];
93         prefilterTexId = allTexIds[3];
94         brdfMapTexId = allTexIds[4];
95         
96         glBindTexture(GL_TEXTURE_2D, hdrTexId);
97
98         glTexImage2D(GL_TEXTURE_2D, 0, img.internalFormat(), img.width(), img.height(), 0, img.format(), img.type(), img.data());
99
100         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
101         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
102         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
103         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
104
105         // generate framebuffers to store cubemap in
106         glGenFramebuffers(1, &captureFBO);
107         glGenRenderbuffers(1, &captureRBO);
108         
109         constexpr GLuint CUBEMAP_WIDTH = 1024, CUBEMAP_HEIGHT = 1024;
110
111         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
112         glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
113         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, CUBEMAP_WIDTH, CUBEMAP_HEIGHT);
114         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureFBO);
115
116         // setup cubemap texture
117         glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
118         for (GLuint i = 0; i < 6; i++)
119                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, CUBEMAP_WIDTH, CUBEMAP_HEIGHT, 0, GL_RGB, GL_HALF_FLOAT, nullptr);
120         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
121         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
122         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
123         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
124         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
125
126         // generate vertices
127         setupVertices(equiProg.progId, cubeArray());
128
129         // render the cube
130
131         glUniform1i(glGetUniformLocation(equiProg.progId, "equirectangularMap"), 0);
132         glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
133         glActiveTexture(GL_TEXTURE0);
134         glBindTexture(GL_TEXTURE_2D, hdrTexId);
135
136         glViewport(0, 0, CUBEMAP_WIDTH, CUBEMAP_HEIGHT);
137         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
138         for (GLuint i = 0; i < 6; i++) {
139                 glUniformMatrix4fv(glGetUniformLocation(equiProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
140                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemapTexId, 0);
141                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
142                 glDrawArrays(GL_TRIANGLES, 0, 36);
143         }
144
145         // setup irradiance map texture
146         glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceTexId);
147         for (GLuint i = 0; i < 6; i++)
148                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, GL_RGB, GL_HALF_FLOAT, nullptr);
149         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
150         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
151         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
152         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
153         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
154
155         // bind framebuffers for rendering irradiance map into
156         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
157         glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
158         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32);
159         
160         Program irradianceProg("skyboxvert.glsl", "irradiancefrag.glsl");
161         glUseProgram(irradianceProg.progId);
162         glUniform1i(glGetUniformLocation(irradianceProg.progId, "environmentMap"), 0);
163         glUniformMatrix4fv(glGetUniformLocation(irradianceProg.progId, "projection"), 1, GL_FALSE, glm::value_ptr(captureProj));
164         glActiveTexture(GL_TEXTURE0);
165         glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
166
167         // generate vertices
168         setupVertices(irradianceProg.progId, cubeArray());
169
170         // render irradiance map
171         glViewport(0, 0, 32, 32);
172         glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
173         for (GLuint i = 0; i < 6; i++) {
174                 glUniformMatrix4fv(glGetUniformLocation(irradianceProg.progId, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));
175                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceTexId, 0);
176                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
177                 glDrawArrays(GL_TRIANGLES, 0, 36);
178         }
179
180         generatePrefilterMap();
181
182         generateBRDFMap();
183
184         // switch back to regular skybox shader
185         glUseProgram(program.progId);
186         glDepthFunc(GL_LEQUAL);
187
188         // reverse so facing inside out
189         vao = setupVertices(program.progId, cubeArray(), true);
190
191         // restore default framebuffer
192         glBindFramebuffer(GL_FRAMEBUFFER, 0);
193 }
194
195 void Skybox::draw(glm::mat4 proj, glm::mat4 view) const {
196         glUseProgram(program.progId);
197
198         glBindVertexArray(vao);
199
200         GLuint projLoc = glGetUniformLocation(program.progId, "projection");
201         glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
202
203         GLuint viewLoc = glGetUniformLocation(program.progId, "view");
204         view = glm::mat4(glm::mat3(view));
205         glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
206
207         glActiveTexture(GL_TEXTURE0);
208         glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
209         glDrawArrays(GL_TRIANGLES, 0, 36);
210 }
211
212 template <std::size_t N>
213 GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> vertices, bool reverse) {
214         GLuint vao;
215         glGenVertexArrays(1, &vao);
216         glBindVertexArray(vao);
217
218         GLuint vbo;
219         glGenBuffers(1, &vbo);
220
221         if (reverse)
222                 std::reverse(vertices.begin(), vertices.end());
223
224         glBindBuffer(GL_ARRAY_BUFFER, vbo);
225         glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
226         
227         GLuint posLoc = glGetAttribLocation(progId, "pos");
228         glEnableVertexAttribArray(posLoc);
229         glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
230         
231         return vao;
232 }