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
This commit is contained in:
parent
4e165c9d6f
commit
c6c69c0656
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<typename T> void bind_resources(draw::detail::PassBase<T> *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;
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
||||
/** \} */
|
||||
|
|
Loading…
Reference in New Issue