From c6e5f631ee26e97385fd5e2e980bad0b461e8953 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 11 Feb 2023 10:52:17 -0800 Subject: Use floating-point textures for IBL to get a proper range of values. --- gfx/include/gfx/render_backend.h | 1 + gfx/src/render/texture.c | 24 +++--- 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 { TextureRG16, TextureRG16F, TextureRGB8, + TextureR11G11B10F, TextureRGBA8, TextureSRGB8, 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) { switch (texture->target) { case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP: - glTexStorage2D(texture->target, levels, internal_format, desc->width, - desc->height); + glTexStorage2D( + texture->target, levels, internal_format, desc->width, desc->height); break; default: assert(false && "Unhandled texture dimension"); @@ -40,13 +40,13 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { // glTexSubImageXD const GLenum format = to_GL_format(desc->format); - const GLenum type = to_GL_type(desc->format); + const GLenum type = to_GL_type(desc->format); switch (texture->target) { case GL_TEXTURE_2D: if (desc->pixels) { - glTexSubImage2D(GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, - /*yoffset=*/0, desc->width, desc->height, format, type, - desc->pixels); + glTexSubImage2D( + GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, + /*yoffset=*/0, desc->width, desc->height, format, type, desc->pixels); } break; case GL_TEXTURE_CUBE_MAP: @@ -72,10 +72,10 @@ bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { // Texture filtering. const bool linear = desc->filtering == LinearFiltering; - GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR - : GL_NEAREST_MIPMAP_NEAREST) - : (linear ? GL_LINEAR : GL_NEAREST); - GLenum mag = linear ? GL_LINEAR : GL_NEAREST; + GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR + : GL_NEAREST_MIPMAP_NEAREST) + : (linear ? GL_LINEAR : GL_NEAREST); + GLenum mag = linear ? GL_LINEAR : GL_NEAREST; glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); @@ -126,6 +126,8 @@ GLenum to_GL_internal_format(TextureFormat format) { return GL_RG16F; case TextureRGB8: return GL_RGB8; + case TextureR11G11B10F: + return GL_R11F_G11F_B10F; case TextureRGBA8: return GL_RGBA8; case TextureSRGB8: @@ -146,6 +148,7 @@ GLenum to_GL_format(TextureFormat format) { case TextureRG16F: return GL_RG; case TextureRGB8: + case TextureR11G11B10F: case TextureSRGB8: return GL_RGB; case TextureRGBA8: @@ -161,6 +164,7 @@ GLenum to_GL_type(TextureFormat format) { switch (format) { case TextureDepth: case TextureRG16F: + case TextureR11G11B10F: return GL_FLOAT; case TextureRG16: 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 @@ #include typedef struct IBL { - Geometry* quad; + Geometry* quad; ShaderProgram* brdf_integration_map_shader; ShaderProgram* irradiance_map_shader; ShaderProgram* prefiltered_environment_map_shader; - Texture* brdf_integration_map; - FrameBuffer* framebuffer; - mat4 rotations[6]; + Texture* brdf_integration_map; + FrameBuffer* framebuffer; + mat4 rotations[6]; } IBL; static const CubemapFace faces[6] = { @@ -71,29 +71,35 @@ IBL* gfx_make_ibl(RenderBackend* render_backend) { } // Right. - ibl->rotations[0] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), - /*target=*/vec3_make(1, 0, 0), - /*up=*/vec3_make(0, 1, 0)); + ibl->rotations[0] = mat4_lookat( + /*position=*/vec3_make(0, 0, 0), + /*target=*/vec3_make(1, 0, 0), + /*up=*/vec3_make(0, 1, 0)); // Left. - ibl->rotations[1] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), - /*target=*/vec3_make(-1, 0, 0), - /*up=*/vec3_make(0, 1, 0)); + ibl->rotations[1] = mat4_lookat( + /*position=*/vec3_make(0, 0, 0), + /*target=*/vec3_make(-1, 0, 0), + /*up=*/vec3_make(0, 1, 0)); // Up. - ibl->rotations[2] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), - /*target=*/vec3_make(0, 1, 0), - /*up=*/vec3_make(0, 0, 1)); + ibl->rotations[2] = mat4_lookat( + /*position=*/vec3_make(0, 0, 0), + /*target=*/vec3_make(0, 1, 0), + /*up=*/vec3_make(0, 0, 1)); // Down. - ibl->rotations[3] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), - /*target=*/vec3_make(0, -1, 0), - /*up=*/vec3_make(0, 0, -1)); + ibl->rotations[3] = mat4_lookat( + /*position=*/vec3_make(0, 0, 0), + /*target=*/vec3_make(0, -1, 0), + /*up=*/vec3_make(0, 0, -1)); // Back. - ibl->rotations[4] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), - /*target=*/vec3_make(0, 0, 1), - /*up=*/vec3_make(0, 1, 0)); + ibl->rotations[4] = mat4_lookat( + /*position=*/vec3_make(0, 0, 0), + /*target=*/vec3_make(0, 0, 1), + /*up=*/vec3_make(0, 1, 0)); // Forward. - ibl->rotations[5] = mat4_lookat(/*position=*/vec3_make(0, 0, 0), - /*target=*/vec3_make(0, 0, -1), - /*up=*/vec3_make(0, 1, 0)); + ibl->rotations[5] = mat4_lookat( + /*position=*/vec3_make(0, 0, 0), + /*target=*/vec3_make(0, 0, -1), + /*up=*/vec3_make(0, 1, 0)); return ibl; @@ -110,15 +116,15 @@ void gfx_destroy_ibl(RenderBackend* render_backend, IBL** ibl) { gfx_destroy_geometry(render_backend, &(*ibl)->quad); } if ((*ibl)->brdf_integration_map_shader) { - gfx_destroy_shader_program(render_backend, - &(*ibl)->brdf_integration_map_shader); + gfx_destroy_shader_program( + render_backend, &(*ibl)->brdf_integration_map_shader); } if ((*ibl)->irradiance_map_shader) { gfx_destroy_shader_program(render_backend, &(*ibl)->irradiance_map_shader); } if ((*ibl)->prefiltered_environment_map_shader) { - gfx_destroy_shader_program(render_backend, - &(*ibl)->prefiltered_environment_map_shader); + gfx_destroy_shader_program( + render_backend, &(*ibl)->prefiltered_environment_map_shader); } if ((*ibl)->brdf_integration_map) { gfx_destroy_texture(render_backend, &(*ibl)->brdf_integration_map); @@ -130,8 +136,8 @@ void gfx_destroy_ibl(RenderBackend* render_backend, IBL** ibl) { *ibl = 0; } -Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend, - int width, int height) { +Texture* gfx_make_brdf_integration_map( + IBL* ibl, RenderBackend* render_backend, int width, int height) { assert(ibl); assert(render_backend); @@ -142,14 +148,15 @@ Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend, bool success = false; if (!(ibl->brdf_integration_map = gfx_make_texture( - render_backend, &(TextureDesc){.width = width, - .height = height, - .depth = 1, - .dimension = Texture2D, - .format = TextureRG16, - .filtering = LinearFiltering, - .wrap = ClampToEdge, - .mipmaps = false}))) { + render_backend, &(TextureDesc){ + .width = width, + .height = height, + .depth = 1, + .dimension = Texture2D, + .format = TextureRG16F, + .filtering = LinearFiltering, + .wrap = ClampToEdge, + .mipmaps = false}))) { goto cleanup; } @@ -157,10 +164,10 @@ Texture* gfx_make_brdf_integration_map(IBL* ibl, RenderBackend* render_backend, gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, width, height); gfx_activate_shader_program(ibl->brdf_integration_map_shader); if (!gfx_framebuffer_attach_colour( - ibl->framebuffer, - &(FrameBufferAttachment){.type = FrameBufferTexture, - .texture.texture = ibl->brdf_integration_map, - .texture.mip_level = 0})) { + ibl->framebuffer, &(FrameBufferAttachment){ + .type = FrameBufferTexture, + .texture.texture = ibl->brdf_integration_map, + .texture.mip_level = 0})) { goto cleanup; } gfx_render_geometry(ibl->quad); @@ -178,27 +185,32 @@ cleanup: } } -Texture* gfx_make_irradiance_map(IBL* ibl, RenderBackend* render_backend, - const Texture* environment_map, int width, - int height) { +Texture* gfx_make_irradiance_map( + IBL* ibl, RenderBackend* render_backend, const Texture* environment_map, + int width, int height) { assert(ibl); assert(render_backend); assert(environment_map); - Texture* irradiance_map = 0; bool success = false; + Texture* irradiance_map = 0; + // TODO: Could define colour-renderable texture formats separately to make // framebuffer creation less error-prone. Or, at the very least, validate the // choice at runtime. + // + // Make sure to use a float colour format to avoid [0,1] clamping when the + // irradiance values are computed! if (!(irradiance_map = gfx_make_texture( - render_backend, &(TextureDesc){.width = width, - .height = height, - .depth = 1, - .dimension = TextureCubeMap, - .format = TextureRGBA8, - .filtering = NearestFiltering, - .mipmaps = false}))) { + render_backend, &(TextureDesc){ + .width = width, + .height = height, + .depth = 1, + .dimension = TextureCubeMap, + .format = TextureR11G11B10F, + .filtering = LinearFiltering, + .mipmaps = false}))) { goto cleanup; } @@ -208,14 +220,14 @@ Texture* gfx_make_irradiance_map(IBL* ibl, RenderBackend* render_backend, gfx_set_texture_uniform(ibl->irradiance_map_shader, "Sky", environment_map); for (int i = 0; i < 6; ++i) { if (!gfx_framebuffer_attach_colour( - ibl->framebuffer, - &(FrameBufferAttachment){.type = FrameBufferCubemapTexture, - .cubemap.face = faces[i], - .cubemap.texture = irradiance_map})) { + ibl->framebuffer, &(FrameBufferAttachment){ + .type = FrameBufferCubemapTexture, + .cubemap.face = faces[i], + .cubemap.texture = irradiance_map})) { goto cleanup; } - gfx_set_mat4_uniform(ibl->irradiance_map_shader, "Modelview", - &ibl->rotations[i]); + gfx_set_mat4_uniform( + ibl->irradiance_map_shader, "Modelview", &ibl->rotations[i]); gfx_apply_uniforms(ibl->irradiance_map_shader); gfx_render_geometry(ibl->quad); } @@ -233,62 +245,62 @@ cleanup: } } -Texture* gfx_make_prefiltered_environment_map(IBL* ibl, - RenderBackend* render_backend, - const Texture* environment_map, - int width, int height, - int* max_mip_level) { +Texture* gfx_make_prefiltered_environment_map( + IBL* ibl, RenderBackend* render_backend, const Texture* environment_map, + int width, int height, int* max_mip_level) { assert(ibl); assert(render_backend); assert(environment_map); assert(max_mip_level); - Texture* prefiltered_env_map = 0; bool success = false; - // TODO: Use 16 bits for more precision and a floating-point format, e.g. - // RGB16F. + Texture* prefiltered_env_map = 0; + if (!(prefiltered_env_map = gfx_make_texture( - render_backend, &(TextureDesc){.width = width, - .height = height, - .depth = 1, - .dimension = TextureCubeMap, - .format = TextureRGBA8, - .filtering = LinearFiltering, - .mipmaps = true}))) { + render_backend, &(TextureDesc){ + .width = width, + .height = height, + .depth = 1, + .dimension = TextureCubeMap, + .format = TextureR11G11B10F, + .filtering = LinearFiltering, + .mipmaps = true}))) { goto cleanup; } gfx_activate_framebuffer(ibl->framebuffer); gfx_activate_shader_program(ibl->prefiltered_environment_map_shader); - gfx_set_texture_uniform(ibl->prefiltered_environment_map_shader, "Sky", - environment_map); + gfx_set_texture_uniform( + ibl->prefiltered_environment_map_shader, "Sky", environment_map); const int max_mip = (int)(rlog2(min(width, height))); for (int mip = 0; mip <= max_mip; ++mip) { - const int mip_width = width >> mip; - const int mip_height = height >> mip; - const float roughness = (float)mip / (float)(max_mip); + const int mip_width = width >> mip; + const int mip_height = height >> mip; + const float roughness = (float)mip / (float)(max_mip); gfx_framebuffer_set_viewport(ibl->framebuffer, 0, 0, mip_width, mip_height); - gfx_set_float_uniform(ibl->prefiltered_environment_map_shader, "Roughness", - roughness); + gfx_set_float_uniform( + ibl->prefiltered_environment_map_shader, "Roughness", roughness); for (int i = 0; i < 6; ++i) { if (!gfx_framebuffer_attach_colour( ibl->framebuffer, &(FrameBufferAttachment){ - .type = FrameBufferCubemapTexture, + .type = FrameBufferCubemapTexture, .cubemap.face = faces[i], .cubemap.mip_level = mip, .cubemap.texture = prefiltered_env_map})) { goto cleanup; } - gfx_set_mat4_uniform(ibl->prefiltered_environment_map_shader, "Modelview", - &ibl->rotations[i]); + gfx_set_mat4_uniform( + ibl->prefiltered_environment_map_shader, "Modelview", + &ibl->rotations[i]); gfx_apply_uniforms(ibl->prefiltered_environment_map_shader); gfx_render_geometry(ibl->quad); } } *max_mip_level = max_mip; + success = true; cleanup: -- cgit v1.2.3