15constexpr float kCubeVertices[] = {
17 -1, -1, -1, 1, 1, -1, 1, -1, -1,
18 -1, -1, -1, -1, 1, -1, 1, 1, -1,
20 -1, -1, 1, 1, -1, 1, 1, 1, 1,
21 -1, -1, 1, 1, 1, 1, -1, 1, 1,
23 -1, -1, -1, -1, -1, 1, -1, 1, 1,
24 -1, -1, -1, -1, 1, 1, -1, 1, -1,
26 1, -1, -1, 1, 1, 1, 1, -1, 1,
27 1, -1, -1, 1, 1, -1, 1, 1, 1,
29 -1, -1, -1, 1, -1, -1, 1, -1, 1,
30 -1, -1, -1, 1, -1, 1, -1, -1, 1,
32 -1, 1, -1, -1, 1, 1, 1, 1, 1,
33 -1, 1, -1, 1, 1, 1, 1, 1, -1,
41 using V3 = Math::Vector<float, 3>;
42 const V3 origin{0.0f, 0.0f, 0.0f};
45 case 0: target = V3{ 1, 0, 0}; up = V3{0, -1, 0};
break;
46 case 1: target = V3{-1, 0, 0}; up = V3{0, -1, 0};
break;
47 case 2: target = V3{ 0, 1, 0}; up = V3{0, 0, 1};
break;
48 case 3: target = V3{ 0,-1, 0}; up = V3{0, 0, -1};
break;
49 case 4: target = V3{ 0, 0, 1}; up = V3{0, -1, 0};
break;
50 case 5: target = V3{ 0, 0,-1}; up = V3{0, -1, 0};
break;
51 default: target = V3{ 1, 0, 0}; up = V3{0, -1, 0};
break;
61 if (m_irradianceCubemap) { glDeleteTextures(1, &m_irradianceCubemap); m_irradianceCubemap = 0; }
62 if (m_prefilterCubemap) { glDeleteTextures(1, &m_prefilterCubemap); m_prefilterCubemap = 0; }
63 if (m_brdfLUT) { glDeleteTextures(1, &m_brdfLUT); m_brdfLUT = 0; }
64 if (m_fbo) { glDeleteFramebuffers(1, &m_fbo); m_fbo = 0; }
65 if (m_rbo) { glDeleteRenderbuffers(1, &m_rbo); m_rbo = 0; }
66 if (m_cubeVBO) { glDeleteBuffers(1, &m_cubeVBO); m_cubeVBO = 0; }
67 if (m_cubeVAO) { glDeleteVertexArrays(1, &m_cubeVAO); m_cubeVAO = 0; }
68 if (m_quadVAO) { glDeleteVertexArrays(1, &m_quadVAO); m_quadVAO = 0; }
69 if (m_faceUBO) { glDeleteBuffers(1, &m_faceUBO); m_faceUBO = 0; }
70 if (m_prefilterUBO) { glDeleteBuffers(1, &m_prefilterUBO); m_prefilterUBO = 0; }
71 delete m_irradianceShader; m_irradianceShader =
nullptr;
72 delete m_prefilterShader; m_prefilterShader =
nullptr;
73 delete m_brdfShader; m_brdfShader =
nullptr;
74 m_initialized =
false;
77bool OpenGLIBL::CreateCubemapTarget(GLuint& tex, uint32_t size, uint32_t mipLevels) {
78 glGenTextures(1, &tex);
79 glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
80 for (
int face = 0; face < 6; ++face) {
81 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGB16F,
82 size, size, 0, GL_RGB, GL_FLOAT,
nullptr);
84 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
85 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
86 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
87 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
88 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
89 mipLevels > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
90 if (mipLevels > 1) glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
91 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
96 if (m_initialized)
return true;
97 if (sourceCubemap == 0) {
98 SLEAK_WARN(
"OpenGLIBL: source cubemap is 0 — IBL precompute skipped");
103 glGenVertexArrays(1, &m_cubeVAO);
104 glGenBuffers(1, &m_cubeVBO);
105 glBindVertexArray(m_cubeVAO);
106 glBindBuffer(GL_ARRAY_BUFFER, m_cubeVBO);
107 glBufferData(GL_ARRAY_BUFFER,
sizeof(kCubeVertices), kCubeVertices, GL_STATIC_DRAW);
108 glEnableVertexAttribArray(0);
109 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 *
sizeof(
float), (
void*)0);
110 glBindVertexArray(0);
113 glGenVertexArrays(1, &m_quadVAO);
116 glGenFramebuffers(1, &m_fbo);
117 glGenRenderbuffers(1, &m_rbo);
120 glGenBuffers(1, &m_faceUBO);
121 glBindBuffer(GL_UNIFORM_BUFFER, m_faceUBO);
122 glBufferData(GL_UNIFORM_BUFFER, 64,
nullptr, GL_DYNAMIC_DRAW);
123 glBindBuffer(GL_UNIFORM_BUFFER, 0);
125 glGenBuffers(1, &m_prefilterUBO);
126 glBindBuffer(GL_UNIFORM_BUFFER, m_prefilterUBO);
127 glBufferData(GL_UNIFORM_BUFFER, 80,
nullptr, GL_DYNAMIC_DRAW);
128 glBindBuffer(GL_UNIFORM_BUFFER, 0);
132 if (!m_irradianceShader->compile(
"assets/shaders/ibl_irradiance_gl.vert",
133 "assets/shaders/ibl_irradiance_gl.frag")) {
134 SLEAK_ERROR(
"OpenGLIBL: failed to compile irradiance shader");
140 if (!m_prefilterShader->compile(
"assets/shaders/ibl_prefilter_gl.vert",
141 "assets/shaders/ibl_prefilter_gl.frag")) {
142 SLEAK_ERROR(
"OpenGLIBL: failed to compile prefilter shader");
148 if (!m_brdfShader->compile(
"assets/shaders/ibl_brdf_lut_gl.vert",
149 "assets/shaders/ibl_brdf_lut_gl.frag")) {
150 SLEAK_ERROR(
"OpenGLIBL: failed to compile BRDF LUT shader");
159 glGenTextures(1, &m_brdfLUT);
160 glBindTexture(GL_TEXTURE_2D, m_brdfLUT);
162 0, GL_RG, GL_FLOAT,
nullptr);
163 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
164 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
165 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
166 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
167 glBindTexture(GL_TEXTURE_2D, 0);
170 GLint prevFBO = 0, prevViewport[4] = {0,0,0,0};
171 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFBO);
172 glGetIntegerv(GL_VIEWPORT, prevViewport);
174 glDisable(GL_DEPTH_TEST);
176 glDisable(GL_CULL_FACE);
178 bool ok = BakeIrradiance(sourceCubemap)
179 && BakePrefilter(sourceCubemap)
183 glBindFramebuffer(GL_FRAMEBUFFER, prevFBO);
184 glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]);
185 glEnable(GL_DEPTH_TEST);
186 glEnable(GL_CULL_FACE);
194 m_initialized =
true;
195 SLEAK_INFO(
"OpenGLIBL: precompute complete (irradiance {}², prefilter {}² × {} mips, BRDF LUT {}²)",
200bool OpenGLIBL::BakeIrradiance(GLuint sourceCubemap) {
202 1.5707963f , 1.0f, 0.1f, 10.0f);
204 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
205 glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
206 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
208 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
209 GL_RENDERBUFFER, m_rbo);
212 m_irradianceShader->
bind();
217 glActiveTexture(GL_TEXTURE0);
218 glBindTexture(GL_TEXTURE_CUBE_MAP, sourceCubemap);
219 GLint envLoc = glGetUniformLocation(m_irradianceShader->
GetProgram(),
"environmentMap");
220 if (envLoc >= 0) glUniform1i(envLoc, 0);
222 glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_faceUBO);
224 glBindVertexArray(m_cubeVAO);
225 for (
int face = 0; face < 6; ++face) {
228 glBindBuffer(GL_UNIFORM_BUFFER, m_faceUBO);
229 glBufferSubData(GL_UNIFORM_BUFFER, 0, 64, &vp(0,0));
231 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
232 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
233 m_irradianceCubemap, 0);
234 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
235 SLEAK_ERROR(
"OpenGLIBL: irradiance FBO incomplete on face {}", face);
238 glClearColor(0,0,0,1);
239 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
240 glDrawArrays(GL_TRIANGLES, 0, 36);
242 glBindVertexArray(0);
246bool OpenGLIBL::BakePrefilter(GLuint sourceCubemap) {
248 1.5707963f , 1.0f, 0.1f, 10.0f);
250 m_prefilterShader->bind();
252 glActiveTexture(GL_TEXTURE0);
253 glBindTexture(GL_TEXTURE_CUBE_MAP, sourceCubemap);
254 GLint envLoc = glGetUniformLocation(m_prefilterShader->GetProgram(),
"environmentMap");
255 if (envLoc >= 0) glUniform1i(envLoc, 0);
257 glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_prefilterUBO);
259 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
260 glBindVertexArray(m_cubeVAO);
262 struct PrefilterUBOData {
270 glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
271 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
273 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
274 GL_RENDERBUFFER, m_rbo);
275 glViewport(0, 0, mipSize, mipSize);
277 float roughness =
static_cast<float>(mip) /
280 for (
int face = 0; face < 6; ++face) {
284 PrefilterUBOData ubo{};
285 std::memcpy(ubo.vp, &vp(0,0), 64);
286 ubo.roughness = roughness;
287 glBindBuffer(GL_UNIFORM_BUFFER, m_prefilterUBO);
288 glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(ubo), &ubo);
290 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
291 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
292 m_prefilterCubemap, mip);
293 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
294 SLEAK_ERROR(
"OpenGLIBL: prefilter FBO incomplete (mip {}, face {})",
298 glClearColor(0,0,0,1);
299 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
300 glDrawArrays(GL_TRIANGLES, 0, 36);
303 glBindVertexArray(0);
307bool OpenGLIBL::BakeBRDFLUT() {
308 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
309 glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
310 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
312 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
313 GL_RENDERBUFFER, m_rbo);
314 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
315 GL_TEXTURE_2D, m_brdfLUT, 0);
316 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
322 glClearColor(0,0,0,1);
323 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
325 m_brdfShader->bind();
326 glBindVertexArray(m_quadVAO);
327 glDrawArrays(GL_TRIANGLES, 0, 3);
328 glBindVertexArray(0);
333 if (!m_initialized)
return;
335 glBindTexture(GL_TEXTURE_CUBE_MAP, m_irradianceCubemap);
337 glBindTexture(GL_TEXTURE_CUBE_MAP, m_prefilterCubemap);
339 glBindTexture(GL_TEXTURE_2D, m_brdfLUT);
340 glActiveTexture(GL_TEXTURE0);
static Matrix< float, 4, 4 > Perspective(float fovY, float aspectRatio, float nearPlane, float farPlane)
static Matrix< float, 4, 4 > LookAt(const Vector< float, 3 > &eye, const Vector< float, 3 > ¢er, const Vector< float, 3 > &up)
static constexpr uint32_t UNIT_BRDF_LUT
static constexpr uint32_t PREFILTER_SIZE
static constexpr uint32_t BRDF_LUT_SIZE
static constexpr uint32_t UNIT_PREFILTER
static constexpr uint32_t UNIT_IRRADIANCE
static constexpr uint32_t PREFILTER_MIPS
bool Initialize(GLuint sourceCubemap)
Bakes the irradiance map, prefiltered specular map, and BRDF LUT from a source cubemap.
static constexpr uint32_t IRRADIANCE_SIZE
GLSL shader program compiled from either a combined source file or separate vertex/fragment files.
GLuint GetProgram() const
void bind() override
Binds this program as the active shader for subsequent draws.
Matrix< float, 4, 4 > Matrix4
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.