From c6c69c06563b917dd5bb9c573152dc479216e94e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 3 Jul 2023 15:14:34 +0200 Subject: [PATCH] Eevee-next: Octahedral mapping Use octahedral mapping to store probe textures. Octahedral textures are easier to sample and will increase performance. When extracting the world probe it will first be rendered into a cubemap. This cubemap will then be remapped using octahedral texture coordinates. * `CaptureView` captures the world light into a cubemap. * `CaptureView` triggers the reflection probe module to update the octahedral texture using the cubemap. * When sampling reflection probes it will convert the (cubemap) direction to octahedral coordinate and read from the octahedral texture. ![image](/attachments/d3331660-f893-41b7-8c17-ae12ddf2ad11) Pull Request: https://projects.blender.org/blender/blender/pulls/109559 --- source/blender/draw/CMakeLists.txt | 2 + .../draw/engines/eevee_next/eevee_defines.hh | 3 ++ .../eevee_next/eevee_reflection_probes.cc | 37 +++++++++++++++---- .../eevee_next/eevee_reflection_probes.hh | 11 +++++- .../draw/engines/eevee_next/eevee_shader.cc | 2 + .../draw/engines/eevee_next/eevee_shader.hh | 2 + .../draw/engines/eevee_next/eevee_view.cc | 7 ++-- .../shaders/eevee_octahedron_lib.glsl | 33 +++++++++++++++++ .../eevee_reflection_probe_eval_lib.glsl | 2 +- .../shaders/eevee_reflection_probe_lib.glsl | 5 ++- .../eevee_reflection_probe_remap_comp.glsl | 19 ++++++++++ .../infos/eevee_reflection_probe_info.hh | 10 ++++- 12 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_octahedron_lib.glsl create mode 100644 source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 174538dc475..b7cbfb9ad96 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -519,9 +519,11 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl engines/eevee_next/shaders/eevee_motion_blur_lib.glsl engines/eevee_next/shaders/eevee_nodetree_lib.glsl + engines/eevee_next/shaders/eevee_octahedron_lib.glsl engines/eevee_next/shaders/eevee_ray_types_lib.glsl engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl + engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl engines/eevee_next/shaders/eevee_sampling_lib.glsl engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl engines/eevee_next/shaders/eevee_shadow_lib.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 895d13c0a51..c9708924de7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -26,6 +26,9 @@ #define CULLING_ZBIN_GROUP_SIZE 1024 #define CULLING_TILE_GROUP_SIZE 256 +/* Reflection Probes. */ +#define REFLECTION_PROBE_GROUP_SIZE 16 + /** * IMPORTANT: Some data packing are tweaked for these values. * Be sure to update them accordingly. diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc index 7876b26a4b5..aac5b964b4d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.cc @@ -10,16 +10,37 @@ namespace blender::eevee { void ReflectionProbeModule::init() { if (!initialized_) { - const int max_mipmap_levels = log(max_resolution_) + 1; - cubemaps_tx_.ensure_cube_array(GPU_RGBA16F, - max_resolution_, - max_probes_, - GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT, - nullptr, - max_mipmap_levels); - GPU_texture_mipmap_mode(cubemaps_tx_, true, true); initialized_ = true; + + const int max_mipmap_levels = log(max_resolution_) + 1; + probes_tx_.ensure_2d_array(GPU_RGBA16F, + int2(max_resolution_), + max_probes_, + GPU_TEXTURE_USAGE_SHADER_WRITE, + nullptr, + max_mipmap_levels); + GPU_texture_mipmap_mode(probes_tx_, true, true); + + /* Cubemap is half of the resolution of the octahedral map. */ + cubemap_tx_.ensure_cube( + GPU_RGBA16F, max_resolution_ / 2, GPU_TEXTURE_USAGE_ATTACHMENT, nullptr, 1); + GPU_texture_mipmap_mode(cubemap_tx_, false, true); + } + + { + PassSimple &pass = remap_ps_; + pass.init(); + pass.shader_set(instance_.shaders.static_shader_get(REFLECTION_PROBE_REMAP)); + pass.bind_texture("cubemap_tx", cubemap_tx_); + pass.bind_image("octahedral_img", probes_tx_); + pass.dispatch(int2(ceil_division(max_resolution_, REFLECTION_PROBE_GROUP_SIZE))); } } +void ReflectionProbeModule::remap_to_octahedral_projection() +{ + instance_.manager->submit(remap_ps_); + GPU_texture_update_mipmap_chain(probes_tx_); +} + } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh index 68b131a4720..ce13f83b57a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh +++ b/source/blender/draw/engines/eevee_next/eevee_reflection_probes.hh @@ -39,7 +39,12 @@ class ReflectionProbeModule { Instance &instance_; - Texture cubemaps_tx_ = {"Probes"}; + /** Texture containing a cubemap used for updating #probes_tx_. */ + Texture cubemap_tx_ = {"Probe.Cubemap"}; + /** Probes texture stored in octahedral mapping. */ + Texture probes_tx_ = {"Probes"}; + + PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"}; bool initialized_ = false; @@ -52,7 +57,7 @@ class ReflectionProbeModule { template void bind_resources(draw::detail::PassBase *pass) { - pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, cubemaps_tx_); + pass->bind_texture(REFLECTION_PROBE_TEX_SLOT, probes_tx_); } void do_world_update_set(bool value) @@ -66,6 +71,8 @@ class ReflectionProbeModule { return do_world_update_; } + void remap_to_octahedral_projection(); + /* Capture View requires access to the cubemaps texture for framebuffer configuration. */ friend class CaptureView; }; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 0c0c484743f..997e4ed9aae 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -156,6 +156,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_lightprobe_irradiance_ray"; case LIGHTPROBE_IRRADIANCE_LOAD: return "eevee_lightprobe_irradiance_load"; + case REFLECTION_PROBE_REMAP: + return "eevee_reflection_probe_remap"; case SHADOW_CLIPMAP_CLEAR: return "eevee_shadow_clipmap_clear"; case SHADOW_DEBUG: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 44a454ac6c8..820904f5b4b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -74,6 +74,8 @@ enum eShaderType { MOTION_BLUR_TILE_FLATTEN_RENDER, MOTION_BLUR_TILE_FLATTEN_VIEWPORT, + REFLECTION_PROBE_REMAP, + SHADOW_CLIPMAP_CLEAR, SHADOW_DEBUG, SHADOW_PAGE_ALLOCATE, diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index fc4618812a1..033855e5d97 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -206,9 +206,8 @@ void CaptureView::render() View view = {"World.Capture.View"}; for (int face : IndexRange(6)) { - capture_fb_.ensure( - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemaps_tx_, face)); + capture_fb_.ensure(GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE_CUBEFACE(inst_.reflection_probes.cubemap_tx_, face)); GPU_framebuffer_bind(capture_fb_); float4x4 view_m4 = cubeface_mat(face); @@ -216,7 +215,7 @@ void CaptureView::render() view.sync(view_m4, win_m4); inst_.pipelines.world.render(view); } - GPU_texture_update_mipmap_chain(inst_.reflection_probes.cubemaps_tx_); + inst_.reflection_probes.remap_to_octahedral_projection(); GPU_debug_group_end(); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_octahedron_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_octahedron_lib.glsl new file mode 100644 index 00000000000..0559dad064e --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_octahedron_lib.glsl @@ -0,0 +1,33 @@ +/** + * Convert from a cubemap vector to an octahedron UV coordinate. + */ +vec2 octahedral_uv_from_direction(vec3 co) +{ + /* Projection onto octahedron. */ + co /= dot(vec3(1.0), abs(co)); + + /* Out-folding of the downward faces. */ + if (co.z < 0.0) { + vec2 sign = step(0.0, co.xy) * 2.0 - 1.0; + co.xy = (1.0 - abs(co.yx)) * sign; + } + + /* Mapping to [0;1]^2 texture space. */ + vec2 uvs = co.xy * (0.5) + 0.5; + + return uvs; +} + +vec3 octahedral_uv_to_direction(vec2 co) +{ + co = co * 2.0 - 1.0; + + vec2 abs_co = abs(co); + vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); + + if (abs_co.x + abs_co.y > 1.0) { + v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); + } + + return v; +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl index 4794e3a82e5..5b71fcec8c9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl @@ -38,7 +38,7 @@ void light_world_eval(ClosureReflection reflection, vec3 P, vec3 V, inout vec3 o /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ /* TODO: lod_factor should be precalculated and stored inside the reflection probe data. */ - const float bias = 0; + const float bias = 0.0; const float lod_factor = bias + 0.5 * log(float(square_i(texture_size.x))) / log(2); /* -2: Don't use LOD levels that are smaller than 4x4 pixels. */ float lod = clamp(lod_factor - 0.5 * log2(pdf * dist), 0.0, lod_cube_max - 2.0); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl index 07968adcaf8..a50d7b5c3ec 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl @@ -1,6 +1,9 @@ + #pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_octahedron_lib.glsl) vec3 light_world_sample(vec3 L, float lod) { - return textureLod_cubemapArray(reflectionProbes, vec4(L, 0.0), lod).rgb; + vec2 octahedral_uv = octahedral_uv_from_direction(L); + return textureLod(reflectionProbes, vec3(octahedral_uv, 0.0), lod).rgb; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl new file mode 100644 index 00000000000..99124d44d3f --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_reflection_probe_remap_comp.glsl @@ -0,0 +1,19 @@ + +/* Shader to convert cubemap to octahedral projection. */ + +#pragma BLENDER_REQUIRE(eevee_octahedron_lib.glsl) + +void main() +{ + ivec3 octahedral_coord = ivec3(gl_GlobalInvocationID.xyz); + ivec3 octahedral_size = imageSize(octahedral_img); + /* Group doesn't fit in output texture. */ + if (any(greaterThanEqual(octahedral_coord.xy, octahedral_size.xy))) { + return; + } + vec2 octahedral_uv = vec2(octahedral_coord) / vec2(octahedral_size); + vec3 R = octahedral_uv_to_direction(octahedral_uv); + + vec4 col = textureLod(cubemap_tx, R, 0.0); + imageStore(octahedral_img, octahedral_coord, col); +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh index e0b7ad8b116..838072f6a55 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_reflection_probe_info.hh @@ -10,6 +10,14 @@ * \{ */ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_data) - .sampler(REFLECTION_PROBE_TEX_SLOT, ImageType::FLOAT_CUBE_ARRAY, "reflectionProbes"); + .sampler(REFLECTION_PROBE_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "reflectionProbes"); + +/* Sample cubemap and remap into an octahedral texture. */ +GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap) + .local_group_size(REFLECTION_PROBE_GROUP_SIZE, REFLECTION_PROBE_GROUP_SIZE) + .sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "octahedral_img") + .compute_source("eevee_reflection_probe_remap_comp.glsl") + .do_static_compilation(true); /** \} */