3 #include <glm/gtc/type_ptr.hpp>
5 template <std::size_t N>
6 GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> vertices, bool reverse = false);
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))
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);
29 glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
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);
38 setupVertices(prefilterProg.progId, cube());
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);
49 float roughness = (float)mip / (MAX_MIP_LEVELS - 1.f);
50 glUniform1f(glGetUniformLocation(prefilterProg.progId, "roughness"), roughness);
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);
61 void Skybox::generateBRDFMap() const {
62 glBindTexture(GL_TEXTURE_2D, brdfMapTexId);
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);
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);
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);
80 setupVertices(prog.progId, plane());
81 glDrawArrays(GL_TRIANGLES, 0, 6);
84 Skybox::Skybox(const Image img): program("skyboxvert.glsl", "skyboxfrag.glsl") {
85 Program equiProg("skyboxvert.glsl", "equirectangularfrag.glsl");
86 glUseProgram(equiProg.progId);
89 glGenTextures(5, allTexIds);
90 hdrTexId = allTexIds[0];
91 cubemapTexId = allTexIds[1];
92 irradianceTexId = allTexIds[2];
93 prefilterTexId = allTexIds[3];
94 brdfMapTexId = allTexIds[4];
96 glBindTexture(GL_TEXTURE_2D, hdrTexId);
98 glTexImage2D(GL_TEXTURE_2D, 0, img.internalFormat(), img.width(), img.height(), 0, img.format(), img.type(), img.data());
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);
105 // generate framebuffers to store cubemap in
106 glGenFramebuffers(1, &captureFBO);
107 glGenRenderbuffers(1, &captureRBO);
109 constexpr GLuint CUBEMAP_WIDTH = 1024, CUBEMAP_HEIGHT = 1024;
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);
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);
127 setupVertices(equiProg.progId, cube());
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);
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);
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);
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);
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);
168 setupVertices(irradianceProg.progId, cube());
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);
180 generatePrefilterMap();
184 // switch back to regular skybox shader
185 glUseProgram(program.progId);
186 glDepthFunc(GL_LEQUAL);
188 // reverse so facing inside out
189 vao = setupVertices(program.progId, cube(), true);
191 // restore default framebuffer
192 glBindFramebuffer(GL_FRAMEBUFFER, 0);
195 void Skybox::draw(glm::mat4 proj, glm::mat4 view) const {
196 glUseProgram(program.progId);
198 glBindVertexArray(vao);
200 GLuint projLoc = glGetUniformLocation(program.progId, "projection");
201 glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
203 GLuint viewLoc = glGetUniformLocation(program.progId, "view");
204 view = glm::mat4(glm::mat3(view));
205 glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
207 glActiveTexture(GL_TEXTURE0);
208 glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexId);
209 glDrawArrays(GL_TRIANGLES, 0, 36);
212 template <std::size_t N>
213 GLuint setupVertices(GLuint progId, std::array<glm::vec3, N> vertices, bool reverse) {
215 glGenVertexArrays(1, &vao);
216 glBindVertexArray(vao);
219 glGenBuffers(1, &vbo);
222 std::reverse(vertices.begin(), vertices.end());
224 glBindBuffer(GL_ARRAY_BUFFER, vbo);
225 glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
227 GLuint posLoc = glGetAttribLocation(progId, "pos");
228 glEnableVertexAttribArray(posLoc);
229 glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);