diff options
-rw-r--r-- | gfx/include/gfx/render_backend.h | 1 | ||||
-rw-r--r-- | gfx/src/render/texture.c | 24 | ||||
-rw-r--r-- | gfx/src/util/ibl.c | 176 |
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 | ||
11 | typedef struct IBL { | 11 | typedef 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 | ||
21 | static const CubemapFace faces[6] = { | 21 | static 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 | ||
133 | Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend, | 139 | Texture* 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 | ||
181 | Texture* gfx_make_irradiance_map(IBL* ibl, RenderBackend* render_backend, | 188 | Texture* 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 | ||
236 | Texture* gfx_make_prefiltered_environment_map(IBL* ibl, | 248 | Texture* 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 | ||
294 | cleanup: | 306 | cleanup: |