summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-02-11 10:52:17 -0800
committer3gg <3gg@shellblade.net>2023-02-11 10:52:17 -0800
commitc6e5f631ee26e97385fd5e2e980bad0b461e8953 (patch)
treec825938aa3e75d379d81e752c69297e8f6110882
parentdbbdcf4be06c05f60798b322890c88b79d802bd8 (diff)
Use floating-point textures for IBL to get a proper range of values.
-rw-r--r--gfx/include/gfx/render_backend.h1
-rw-r--r--gfx/src/render/texture.c24
-rw-r--r--gfx/src/util/ibl.c176
3 files changed, 109 insertions, 92 deletions
diff --git a/gfx/include/gfx/render_backend.h b/gfx/include/gfx/render_backend.h
index 167cf8a..e037f78 100644
--- a/gfx/include/gfx/render_backend.h
+++ b/gfx/include/gfx/render_backend.h
@@ -159,6 +159,7 @@ typedef enum {
159 TextureRG16, 159 TextureRG16,
160 TextureRG16F, 160 TextureRG16F,
161 TextureRGB8, 161 TextureRGB8,
162 TextureR11G11B10F,
162 TextureRGBA8, 163 TextureRGBA8,
163 TextureSRGB8, 164 TextureSRGB8,
164 TextureSRGBA8 165 TextureSRGBA8
diff --git a/gfx/src/render/texture.c b/gfx/src/render/texture.c
index 691f3f8..31bf636 100644
--- a/gfx/src/render/texture.c
+++ b/gfx/src/render/texture.c
@@ -29,8 +29,8 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) {
29 switch (texture->target) { 29 switch (texture->target) {
30 case GL_TEXTURE_2D: 30 case GL_TEXTURE_2D:
31 case GL_TEXTURE_CUBE_MAP: 31 case GL_TEXTURE_CUBE_MAP:
32 glTexStorage2D(texture->target, levels, internal_format, desc->width, 32 glTexStorage2D(
33 desc->height); 33 texture->target, levels, internal_format, desc->width, desc->height);
34 break; 34 break;
35 default: 35 default:
36 assert(false && "Unhandled texture dimension"); 36 assert(false && "Unhandled texture dimension");
@@ -40,13 +40,13 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) {
40 40
41 // glTexSubImageXD 41 // glTexSubImageXD
42 const GLenum format = to_GL_format(desc->format); 42 const GLenum format = to_GL_format(desc->format);
43 const GLenum type = to_GL_type(desc->format); 43 const GLenum type = to_GL_type(desc->format);
44 switch (texture->target) { 44 switch (texture->target) {
45 case GL_TEXTURE_2D: 45 case GL_TEXTURE_2D:
46 if (desc->pixels) { 46 if (desc->pixels) {
47 glTexSubImage2D(GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, 47 glTexSubImage2D(
48 /*yoffset=*/0, desc->width, desc->height, format, type, 48 GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0,
49 desc->pixels); 49 /*yoffset=*/0, desc->width, desc->height, format, type, desc->pixels);
50 } 50 }
51 break; 51 break;
52 case GL_TEXTURE_CUBE_MAP: 52 case GL_TEXTURE_CUBE_MAP:
@@ -72,10 +72,10 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) {
72 72
73 // Texture filtering. 73 // Texture filtering.
74 const bool linear = desc->filtering == LinearFiltering; 74 const bool linear = desc->filtering == LinearFiltering;
75 GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR 75 GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR
76 : GL_NEAREST_MIPMAP_NEAREST) 76 : GL_NEAREST_MIPMAP_NEAREST)
77 : (linear ? GL_LINEAR : GL_NEAREST); 77 : (linear ? GL_LINEAR : GL_NEAREST);
78 GLenum mag = linear ? GL_LINEAR : GL_NEAREST; 78 GLenum mag = linear ? GL_LINEAR : GL_NEAREST;
79 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); 79 glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min);
80 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); 80 glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag);
81 81
@@ -126,6 +126,8 @@ GLenum to_GL_internal_format(TextureFormat format) {
126 return GL_RG16F; 126 return GL_RG16F;
127 case TextureRGB8: 127 case TextureRGB8:
128 return GL_RGB8; 128 return GL_RGB8;
129 case TextureR11G11B10F:
130 return GL_R11F_G11F_B10F;
129 case TextureRGBA8: 131 case TextureRGBA8:
130 return GL_RGBA8; 132 return GL_RGBA8;
131 case TextureSRGB8: 133 case TextureSRGB8:
@@ -146,6 +148,7 @@ GLenum to_GL_format(TextureFormat format) {
146 case TextureRG16F: 148 case TextureRG16F:
147 return GL_RG; 149 return GL_RG;
148 case TextureRGB8: 150 case TextureRGB8:
151 case TextureR11G11B10F:
149 case TextureSRGB8: 152 case TextureSRGB8:
150 return GL_RGB; 153 return GL_RGB;
151 case TextureRGBA8: 154 case TextureRGBA8:
@@ -161,6 +164,7 @@ GLenum to_GL_type(TextureFormat format) {
161 switch (format) { 164 switch (format) {
162 case TextureDepth: 165 case TextureDepth:
163 case TextureRG16F: 166 case TextureRG16F:
167 case TextureR11G11B10F:
164 return GL_FLOAT; 168 return GL_FLOAT;
165 case TextureRG16: 169 case TextureRG16:
166 case TextureRGB8: 170 case TextureRGB8:
diff --git a/gfx/src/util/ibl.c b/gfx/src/util/ibl.c
index 704cf08..c9bef43 100644
--- a/gfx/src/util/ibl.c
+++ b/gfx/src/util/ibl.c
@@ -9,13 +9,13 @@
9#include <stdlib.h> 9#include <stdlib.h>
10 10
11typedef struct IBL { 11typedef struct IBL {
12 Geometry* quad; 12 Geometry* quad;
13 ShaderProgram* brdf_integration_map_shader; 13 ShaderProgram* brdf_integration_map_shader;
14 ShaderProgram* irradiance_map_shader; 14 ShaderProgram* irradiance_map_shader;
15 ShaderProgram* prefiltered_environment_map_shader; 15 ShaderProgram* prefiltered_environment_map_shader;
16 Texture* brdf_integration_map; 16 Texture* brdf_integration_map;
17 FrameBuffer* framebuffer; 17 FrameBuffer* framebuffer;
18 mat4 rotations[6]; 18 mat4 rotations[6];
19} IBL; 19} IBL;
20 20
21static const CubemapFace faces[6] = { 21static const CubemapFace faces[6] = {
@@ -71,29 +71,35 @@ IBL* gfx_make_ibl(RenderBackend* render_backend) {
71 } 71 }
72 72
73 // Right. 73 // Right.
74 ibl->rotations[0] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), 74 ibl->rotations[0] = mat4_lookat(
75 /*target=*/vec3_make(1, 0, 0), 75 /*position=*/vec3_make(0, 0, 0),
76 /*up=*/vec3_make(0, 1, 0)); 76 /*target=*/vec3_make(1, 0, 0),
77 /*up=*/vec3_make(0, 1, 0));
77 // Left. 78 // Left.
78 ibl->rotations[1] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), 79 ibl->rotations[1] = mat4_lookat(
79 /*target=*/vec3_make(-1, 0, 0), 80 /*position=*/vec3_make(0, 0, 0),
80 /*up=*/vec3_make(0, 1, 0)); 81 /*target=*/vec3_make(-1, 0, 0),
82 /*up=*/vec3_make(0, 1, 0));
81 // Up. 83 // Up.
82 ibl->rotations[2] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), 84 ibl->rotations[2] = mat4_lookat(
83 /*target=*/vec3_make(0, 1, 0), 85 /*position=*/vec3_make(0, 0, 0),
84 /*up=*/vec3_make(0, 0, 1)); 86 /*target=*/vec3_make(0, 1, 0),
87 /*up=*/vec3_make(0, 0, 1));
85 // Down. 88 // Down.
86 ibl->rotations[3] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), 89 ibl->rotations[3] = mat4_lookat(
87 /*target=*/vec3_make(0, -1, 0), 90 /*position=*/vec3_make(0, 0, 0),
88 /*up=*/vec3_make(0, 0, -1)); 91 /*target=*/vec3_make(0, -1, 0),
92 /*up=*/vec3_make(0, 0, -1));
89 // Back. 93 // Back.
90 ibl->rotations[4] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), 94 ibl->rotations[4] = mat4_lookat(
91 /*target=*/vec3_make(0, 0, 1), 95 /*position=*/vec3_make(0, 0, 0),
92 /*up=*/vec3_make(0, 1, 0)); 96 /*target=*/vec3_make(0, 0, 1),
97 /*up=*/vec3_make(0, 1, 0));
93 // Forward. 98 // Forward.
94 ibl->rotations[5] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), 99 ibl->rotations[5] = mat4_lookat(
95 /*target=*/vec3_make(0, 0, -1), 100 /*position=*/vec3_make(0, 0, 0),
96 /*up=*/vec3_make(0, 1, 0)); 101 /*target=*/vec3_make(0, 0, -1),
102 /*up=*/vec3_make(0, 1, 0));
97 103
98 return ibl; 104 return ibl;
99 105
@@ -110,15 +116,15 @@ void gfx_destroy_ibl(RenderBackend* render_backend, IBL** ibl) {
110 gfx_destroy_geometry(render_backend, &(*ibl)->quad); 116 gfx_destroy_geometry(render_backend, &(*ibl)->quad);
111 } 117 }
112 if ((*ibl)->brdf_integration_map_shader) { 118 if ((*ibl)->brdf_integration_map_shader) {
113 gfx_destroy_shader_program(render_backend, 119 gfx_destroy_shader_program(
114 &(*ibl)->brdf_integration_map_shader); 120 render_backend, &(*ibl)->brdf_integration_map_shader);
115 } 121 }
116 if ((*ibl)->irradiance_map_shader) { 122 if ((*ibl)->irradiance_map_shader) {
117 gfx_destroy_shader_program(render_backend, &(*ibl)->irradiance_map_shader); 123 gfx_destroy_shader_program(render_backend, &(*ibl)->irradiance_map_shader);
118 } 124 }
119 if ((*ibl)->prefiltered_environment_map_shader) { 125 if ((*ibl)->prefiltered_environment_map_shader) {
120 gfx_destroy_shader_program(render_backend, 126 gfx_destroy_shader_program(
121 &(*ibl)->prefiltered_environment_map_shader); 127 render_backend, &(*ibl)->prefiltered_environment_map_shader);
122 } 128 }
123 if ((*ibl)->brdf_integration_map) { 129 if ((*ibl)->brdf_integration_map) {
124 gfx_destroy_texture(render_backend, &(*ibl)->brdf_integration_map); 130 gfx_destroy_texture(render_backend, &(*ibl)->brdf_integration_map);
@@ -130,8 +136,8 @@ void gfx_destroy_ibl(RenderBackend* render_backend, IBL** ibl) {
130 *ibl = 0; 136 *ibl = 0;
131} 137}
132 138
133Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend, 139Texture* gfx_make_brdf_integration_map(
134 int width, int height) { 140 IBL* ibl, RenderBackend* render_backend, int width, int height) {
135 assert(ibl); 141 assert(ibl);
136 assert(render_backend); 142 assert(render_backend);
137 143
@@ -142,14 +148,15 @@ Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend,
142 bool success = false; 148 bool success = false;
143 149
144 if (!(ibl->brdf_integration_map = gfx_make_texture( 150 if (!(ibl->brdf_integration_map = gfx_make_texture(
145 render_backend, &(TextureDesc){.width = width, 151 render_backend, &(TextureDesc){
146 .height = height, 152 .width = width,
147 .depth = 1, 153 .height = height,
148 .dimension = Texture2D, 154 .depth = 1,
149 .format = TextureRG16, 155 .dimension = Texture2D,
150 .filtering = LinearFiltering, 156 .format = TextureRG16F,
151 .wrap = ClampToEdge, 157 .filtering = LinearFiltering,
152 .mipmaps = false}))) { 158 .wrap = ClampToEdge,
159 .mipmaps = false}))) {
153 goto cleanup; 160 goto cleanup;
154 } 161 }
155 162
@@ -157,10 +164,10 @@ Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend,
157 gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, width, height); 164 gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, width, height);
158 gfx_activate_shader_program(ibl->brdf_integration_map_shader); 165 gfx_activate_shader_program(ibl->brdf_integration_map_shader);
159 if (!gfx_framebuffer_attach_colour( 166 if (!gfx_framebuffer_attach_colour(
160 ibl->framebuffer, 167 ibl->framebuffer, &(FrameBufferAttachment){
161 &(FrameBufferAttachment){.type = FrameBufferTexture, 168 .type = FrameBufferTexture,
162 .texture.texture = ibl->brdf_integration_map, 169 .texture.texture = ibl->brdf_integration_map,
163 .texture.mip_level = 0})) { 170 .texture.mip_level = 0})) {
164 goto cleanup; 171 goto cleanup;
165 } 172 }
166 gfx_render_geometry(ibl->quad); 173 gfx_render_geometry(ibl->quad);
@@ -178,27 +185,32 @@ cleanup:
178 } 185 }
179} 186}
180 187
181Texture* gfx_make_irradiance_map(IBL* ibl, RenderBackend* render_backend, 188Texture* gfx_make_irradiance_map(
182 const Texture* environment_map, int width, 189 IBL* ibl, RenderBackend* render_backend, const Texture* environment_map,
183 int height) { 190 int width, int height) {
184 assert(ibl); 191 assert(ibl);
185 assert(render_backend); 192 assert(render_backend);
186 assert(environment_map); 193 assert(environment_map);
187 194
188 Texture* irradiance_map = 0;
189 bool success = false; 195 bool success = false;
190 196
197 Texture* irradiance_map = 0;
198
191 // TODO: Could define colour-renderable texture formats separately to make 199 // TODO: Could define colour-renderable texture formats separately to make
192 // framebuffer creation less error-prone. Or, at the very least, validate the 200 // framebuffer creation less error-prone. Or, at the very least, validate the
193 // choice at runtime. 201 // choice at runtime.
202 //
203 // Make sure to use a float colour format to avoid [0,1] clamping when the
204 // irradiance values are computed!
194 if (!(irradiance_map = gfx_make_texture( 205 if (!(irradiance_map = gfx_make_texture(
195 render_backend, &(TextureDesc){.width = width, 206 render_backend, &(TextureDesc){
196 .height = height, 207 .width = width,
197 .depth = 1, 208 .height = height,
198 .dimension = TextureCubeMap, 209 .depth = 1,
199 .format = TextureRGBA8, 210 .dimension = TextureCubeMap,
200 .filtering = NearestFiltering, 211 .format = TextureR11G11B10F,
201 .mipmaps = false}))) { 212 .filtering = LinearFiltering,
213 .mipmaps = false}))) {
202 goto cleanup; 214 goto cleanup;
203 } 215 }
204 216
@@ -208,14 +220,14 @@ Texture* gfx_make_irradiance_map(IBL* ibl, RenderBackend* render_backend,
208 gfx_set_texture_uniform(ibl->irradiance_map_shader, "Sky", environment_map); 220 gfx_set_texture_uniform(ibl->irradiance_map_shader, "Sky", environment_map);
209 for (int i = 0; i < 6; ++i) { 221 for (int i = 0; i < 6; ++i) {
210 if (!gfx_framebuffer_attach_colour( 222 if (!gfx_framebuffer_attach_colour(
211 ibl->framebuffer, 223 ibl->framebuffer, &(FrameBufferAttachment){
212 &(FrameBufferAttachment){.type = FrameBufferCubemapTexture, 224 .type = FrameBufferCubemapTexture,
213 .cubemap.face = faces[i], 225 .cubemap.face = faces[i],
214 .cubemap.texture = irradiance_map})) { 226 .cubemap.texture = irradiance_map})) {
215 goto cleanup; 227 goto cleanup;
216 } 228 }
217 gfx_set_mat4_uniform(ibl->irradiance_map_shader, "Modelview", 229 gfx_set_mat4_uniform(
218 &ibl->rotations[i]); 230 ibl->irradiance_map_shader, "Modelview", &ibl->rotations[i]);
219 gfx_apply_uniforms(ibl->irradiance_map_shader); 231 gfx_apply_uniforms(ibl->irradiance_map_shader);
220 gfx_render_geometry(ibl->quad); 232 gfx_render_geometry(ibl->quad);
221 } 233 }
@@ -233,62 +245,62 @@ cleanup:
233 } 245 }
234} 246}
235 247
236Texture* gfx_make_prefiltered_environment_map(IBL* ibl, 248Texture* gfx_make_prefiltered_environment_map(
237 RenderBackend* render_backend, 249 IBL* ibl, RenderBackend* render_backend, const Texture* environment_map,
238 const Texture* environment_map, 250 int width, int height, int* max_mip_level) {
239 int width, int height,
240 int* max_mip_level) {
241 assert(ibl); 251 assert(ibl);
242 assert(render_backend); 252 assert(render_backend);
243 assert(environment_map); 253 assert(environment_map);
244 assert(max_mip_level); 254 assert(max_mip_level);
245 255
246 Texture* prefiltered_env_map = 0;
247 bool success = false; 256 bool success = false;
248 257
249 // TODO: Use 16 bits for more precision and a floating-point format, e.g. 258 Texture* prefiltered_env_map = 0;
250 // RGB16F. 259
251 if (!(prefiltered_env_map = gfx_make_texture( 260 if (!(prefiltered_env_map = gfx_make_texture(
252 render_backend, &(TextureDesc){.width = width, 261 render_backend, &(TextureDesc){
253 .height = height, 262 .width = width,
254 .depth = 1, 263 .height = height,
255 .dimension = TextureCubeMap, 264 .depth = 1,
256 .format = TextureRGBA8, 265 .dimension = TextureCubeMap,
257 .filtering = LinearFiltering, 266 .format = TextureR11G11B10F,
258 .mipmaps = true}))) { 267 .filtering = LinearFiltering,
268 .mipmaps = true}))) {
259 goto cleanup; 269 goto cleanup;
260 } 270 }
261 271
262 gfx_activate_framebuffer(ibl->framebuffer); 272 gfx_activate_framebuffer(ibl->framebuffer);
263 gfx_activate_shader_program(ibl->prefiltered_environment_map_shader); 273 gfx_activate_shader_program(ibl->prefiltered_environment_map_shader);
264 gfx_set_texture_uniform(ibl->prefiltered_environment_map_shader, "Sky", 274 gfx_set_texture_uniform(
265 environment_map); 275 ibl->prefiltered_environment_map_shader, "Sky", environment_map);
266 const int max_mip = (int)(rlog2(min(width, height))); 276 const int max_mip = (int)(rlog2(min(width, height)));
267 for (int mip = 0; mip <= max_mip; ++mip) { 277 for (int mip = 0; mip <= max_mip; ++mip) {
268 const int mip_width = width >> mip; 278 const int mip_width = width >> mip;
269 const int mip_height = height >> mip; 279 const int mip_height = height >> mip;
270 const float roughness = (float)mip / (float)(max_mip); 280 const float roughness = (float)mip / (float)(max_mip);
271 gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, mip_width, mip_height); 281 gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, mip_width, mip_height);
272 gfx_set_float_uniform(ibl->prefiltered_environment_map_shader, "Roughness", 282 gfx_set_float_uniform(
273 roughness); 283 ibl->prefiltered_environment_map_shader, "Roughness", roughness);
274 284
275 for (int i = 0; i < 6; ++i) { 285 for (int i = 0; i < 6; ++i) {
276 if (!gfx_framebuffer_attach_colour( 286 if (!gfx_framebuffer_attach_colour(
277 ibl->framebuffer, &(FrameBufferAttachment){ 287 ibl->framebuffer, &(FrameBufferAttachment){
278 .type = FrameBufferCubemapTexture, 288 .type = FrameBufferCubemapTexture,
279 .cubemap.face = faces[i], 289 .cubemap.face = faces[i],
280 .cubemap.mip_level = mip, 290 .cubemap.mip_level = mip,
281 .cubemap.texture = prefiltered_env_map})) { 291 .cubemap.texture = prefiltered_env_map})) {
282 goto cleanup; 292 goto cleanup;
283 } 293 }
284 gfx_set_mat4_uniform(ibl->prefiltered_environment_map_shader, "Modelview", 294 gfx_set_mat4_uniform(
285 &ibl->rotations[i]); 295 ibl->prefiltered_environment_map_shader, "Modelview",
296 &ibl->rotations[i]);
286 gfx_apply_uniforms(ibl->prefiltered_environment_map_shader); 297 gfx_apply_uniforms(ibl->prefiltered_environment_map_shader);
287 gfx_render_geometry(ibl->quad); 298 gfx_render_geometry(ibl->quad);
288 } 299 }
289 } 300 }
290 301
291 *max_mip_level = max_mip; 302 *max_mip_level = max_mip;
303
292 success = true; 304 success = true;
293 305
294cleanup: 306cleanup: