EEVEE-Next: Simplify sphere probe storage
- Remove unlimited mip level. - Make computation of sampling region simpler. - Add correct mirroring of UV and border region. - Fix crash when world probe is smaller than lightprobes.
This commit is contained in:
parent
29ec924f9f
commit
f70b8f76e9
@ -36,7 +36,6 @@
|
||||
/* Number of additional pixels on the border of an octahedral map to reserve for fixing seams.
|
||||
* Border size requires depends on the max number of mipmap levels. */
|
||||
#define REFLECTION_PROBE_MIPMAP_LEVELS 5
|
||||
#define REFLECTION_PROBE_BORDER_SIZE float(1 << (REFLECTION_PROBE_MIPMAP_LEVELS - 1))
|
||||
#define REFLECTION_PROBE_SH_GROUP_SIZE 512
|
||||
#define REFLECTION_PROBE_SH_SAMPLES_PER_GROUP 64
|
||||
|
||||
|
@ -167,7 +167,7 @@ void ReflectionProbeModule::init()
|
||||
1,
|
||||
GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
nullptr,
|
||||
9999);
|
||||
REFLECTION_PROBE_MIPMAP_LEVELS);
|
||||
GPU_texture_mipmap_mode(probes_tx_, true, true);
|
||||
probes_tx_.clear(float4(0.0f));
|
||||
}
|
||||
@ -177,11 +177,11 @@ void ReflectionProbeModule::init()
|
||||
pass.init();
|
||||
pass.shader_set(instance_.shaders.static_shader_get(REFLECTION_PROBE_REMAP));
|
||||
pass.bind_texture("cubemap_tx", &cubemap_tx_);
|
||||
pass.bind_image("atlas_dst_mip_img", &atlas_dst_mip_tx_);
|
||||
pass.bind_image("atlas_src_mip_img", &atlas_src_mip_tx_);
|
||||
pass.bind_texture("atlas_tx", &probes_tx_);
|
||||
pass.bind_image("atlas_img", &probes_tx_);
|
||||
pass.push_constant("probe_coord_packed", reinterpret_cast<int4 *>(&probe_sampling_coord_));
|
||||
pass.push_constant("write_coord_packed", reinterpret_cast<int4 *>(&probe_write_coord_));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_write_coord_));
|
||||
pass.push_constant("world_coord_packed", reinterpret_cast<int4 *>(&world_sampling_coord_));
|
||||
pass.push_constant("mip_level", &probe_mip_level_);
|
||||
pass.dispatch(&dispatch_probe_pack_);
|
||||
}
|
||||
@ -468,21 +468,13 @@ std::optional<ReflectionProbeUpdateInfo> ReflectionProbeModule::update_info_pop(
|
||||
void ReflectionProbeModule::remap_to_octahedral_projection(
|
||||
const ReflectionProbeAtlasCoordinate &atlas_coord)
|
||||
{
|
||||
ReflectionProbe &world_probe = probes_.lookup(world_object_key_);
|
||||
|
||||
int world_layer_subdivision = world_probe.atlas_coord.layer_subdivision;
|
||||
int resolution = max_resolution_ >> atlas_coord.layer_subdivision;
|
||||
/* Update shader parameters that change per dispatch. */
|
||||
probe_sampling_coord_ = atlas_coord.as_sampling_coord(atlas_extent());
|
||||
probe_write_coord_ = atlas_coord.as_write_coord(atlas_extent(), 0);
|
||||
probe_mip_level_ = atlas_coord.layer_subdivision;
|
||||
world_write_coord_ = world_probe.atlas_coord.as_write_coord(atlas_extent(), probe_mip_level_);
|
||||
dispatch_probe_pack_ = int3(int2(ceil_division(resolution, REFLECTION_PROBE_GROUP_SIZE)), 1);
|
||||
|
||||
probes_tx_.ensure_mip_views();
|
||||
atlas_dst_mip_tx_ = probes_tx_.mip_view(0);
|
||||
atlas_src_mip_tx_ = probes_tx_.mip_view(probe_mip_level_ - world_layer_subdivision);
|
||||
|
||||
instance_.manager->submit(remap_ps_);
|
||||
}
|
||||
|
||||
|
@ -64,16 +64,34 @@ struct ReflectionProbeAtlasCoordinate {
|
||||
|
||||
ReflectionProbeCoordinate as_sampling_coord(int atlas_extent) const
|
||||
{
|
||||
const int area_count_per_dimension = 1 << layer_subdivision;
|
||||
const float area_scale = 1.0f / area_count_per_dimension;
|
||||
const float2 area_location = float2(this->area_location());
|
||||
|
||||
float texel_size = 1.0f / atlas_extent;
|
||||
float border_size = REFLECTION_PROBE_BORDER_SIZE * texel_size;
|
||||
|
||||
/**
|
||||
* We want to cover the last mip exactly at the pixel center to reduce padding texels and
|
||||
* interpolation artifacts.
|
||||
* This is a diagram of a 2px^2 map with `c` being the texels corners and `x` the pixels
|
||||
* centers.
|
||||
*
|
||||
* c-------c-------c
|
||||
* | | |
|
||||
* | x | x | <
|
||||
* | | | |
|
||||
* c-------c-------c | sampling area
|
||||
* | | | |
|
||||
* | x | x | <
|
||||
* | | |
|
||||
* c-------c-------c
|
||||
* ^-------^
|
||||
* sampling area
|
||||
*/
|
||||
/* First level only need half a pixel of padding around the sampling area. */
|
||||
const int mip_max_lvl_padding = 1;
|
||||
const int mip_min_lvl_padding = mip_max_lvl_padding << REFLECTION_PROBE_MIPMAP_LEVELS;
|
||||
/* Extent and offset in mip 0 texels. */
|
||||
const int sampling_area_extent = area_extent(atlas_extent) - mip_min_lvl_padding;
|
||||
const int2 sampling_area_offset = area_offset(atlas_extent) + mip_min_lvl_padding / 2;
|
||||
/* Convert to atlas UVs. */
|
||||
ReflectionProbeCoordinate coord;
|
||||
coord.offset = (border_size + 0.5f * texel_size + area_location) * area_scale;
|
||||
coord.scale = (1.0f - 2.0f * border_size) * area_scale;
|
||||
coord.scale = sampling_area_extent / float(atlas_extent);
|
||||
coord.offset = float2(sampling_area_offset) / float(atlas_extent);
|
||||
coord.layer = layer;
|
||||
return coord;
|
||||
}
|
||||
@ -160,9 +178,6 @@ class ReflectionProbeModule {
|
||||
|
||||
/** Probes texture stored in octahedral mapping. */
|
||||
Texture probes_tx_ = {"Probes"};
|
||||
/* Reference to a specific mip map layer of a texture. */
|
||||
GPUTexture *atlas_dst_mip_tx_ = nullptr;
|
||||
GPUTexture *atlas_src_mip_tx_ = nullptr;
|
||||
|
||||
PassSimple remap_ps_ = {"Probe.CubemapToOctahedral"};
|
||||
PassSimple update_irradiance_ps_ = {"Probe.UpdateIrradiance"};
|
||||
@ -186,7 +201,6 @@ class ReflectionProbeModule {
|
||||
ReflectionProbeWriteCoordinate probe_write_coord_;
|
||||
/** World coordinates in the atlas. */
|
||||
ReflectionProbeCoordinate world_sampling_coord_;
|
||||
ReflectionProbeWriteCoordinate world_write_coord_;
|
||||
/** Number of the probe to process in the select phase. */
|
||||
int reflection_probe_count_ = 0;
|
||||
|
||||
|
@ -93,7 +93,7 @@ float lightprobe_roughness_to_cube_sh_mix_fac(float roughness)
|
||||
float lightprobe_roughness_to_lod(float roughness)
|
||||
{
|
||||
/* Temporary. Do something better. */
|
||||
return sqrt(roughness) * 11.0;
|
||||
return sqrt(roughness) * REFLECTION_PROBE_MIPMAP_LEVELS;
|
||||
}
|
||||
|
||||
vec3 lightprobe_eval(LightProbeSample samp, ClosureDiffuse cl, vec3 P, vec3 V)
|
||||
|
@ -36,22 +36,3 @@ vec3 octahedral_uv_to_direction(vec2 co)
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the octahedral uv coordinates for the given texture uv coordinate on the packed
|
||||
* octahedral texture layer for the given probe.
|
||||
*
|
||||
* It also applies wrapping in the additional space near borders.
|
||||
* NOTE: Doesn't apply the translation part of the packing.
|
||||
*/
|
||||
vec2 octahedral_uv_from_layer_texture_coords(vec2 uv, vec2 texel_size)
|
||||
{
|
||||
/* Apply border region. */
|
||||
vec2 shrinked_uv = (uv - REFLECTION_PROBE_BORDER_SIZE * texel_size) /
|
||||
(1.0 - 2.0 * REFLECTION_PROBE_BORDER_SIZE * texel_size);
|
||||
/* Use ping/pong to extend the octahedral coordinates. */
|
||||
vec2 translated_pos = clamp(-sign(shrinked_uv), vec2(0.0), vec2(1.0)) * vec2(2.0) + shrinked_uv;
|
||||
ivec2 checker_pos = ivec2(translated_pos);
|
||||
bool is_even = ((checker_pos.x + checker_pos.y) & 1) == 0;
|
||||
return is_even ? fract(shrinked_uv) : vec2(1.0) - fract(shrinked_uv);
|
||||
}
|
||||
|
@ -24,11 +24,24 @@ ReflectionProbeWriteCoordinate reinterpret_as_write_coord(ivec4 packed_coord)
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
/* Mirror the UV if they are not on the diagonal or unit UV squares.
|
||||
* Doesn't extend outside of [-1..2] range. But this is fine since we use it only for borders. */
|
||||
vec2 mirror_repeat_uv(vec2 uv)
|
||||
{
|
||||
vec2 m = abs(uv - 0.5) + 0.5;
|
||||
vec2 f = floor(m);
|
||||
float x = f.x - f.y;
|
||||
if (x != 0.0) {
|
||||
uv.xy = 1.0 - uv.xy;
|
||||
}
|
||||
return fract(uv);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ReflectionProbeCoordinate world_coord = reinterpret_as_atlas_coord(world_coord_packed);
|
||||
ReflectionProbeCoordinate sample_coord = reinterpret_as_atlas_coord(probe_coord_packed);
|
||||
ReflectionProbeWriteCoordinate write_coord = reinterpret_as_write_coord(write_coord_packed);
|
||||
ReflectionProbeWriteCoordinate world_coord = reinterpret_as_write_coord(world_coord_packed);
|
||||
|
||||
/* Texel in probe. */
|
||||
ivec2 local_texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
@ -41,11 +54,12 @@ void main()
|
||||
/* Texel in probe atlas. */
|
||||
ivec2 texel = local_texel + write_coord.offset;
|
||||
/* UV in probe atlas. */
|
||||
vec2 atlas_uv = (vec2(texel) + 0.5) / vec2(imageSize(atlas_dst_mip_img).xy);
|
||||
vec2 atlas_uv = (vec2(texel) + 0.5) / vec2(imageSize(atlas_img).xy);
|
||||
/* UV in sampling area. */
|
||||
vec2 sampling_uv = (atlas_uv - sample_coord.offset) / sample_coord.scale;
|
||||
vec2 wrapped_uv = mirror_repeat_uv(sampling_uv);
|
||||
/* Direction in world space. */
|
||||
vec3 direction = octahedral_uv_to_direction(sampling_uv);
|
||||
vec3 direction = octahedral_uv_to_direction(wrapped_uv);
|
||||
vec4 col = textureLod(cubemap_tx, direction, float(mip_level));
|
||||
|
||||
/* Convert transmittance to transparency. */
|
||||
@ -54,10 +68,10 @@ void main()
|
||||
/* Composite world into reflection probes. */
|
||||
bool is_world = all(equal(write_coord_packed, world_coord_packed));
|
||||
if (!is_world && col.a != 1.0) {
|
||||
ivec2 world_texel = local_texel + world_coord.offset;
|
||||
vec4 world_col = imageLoad(atlas_src_mip_img, ivec3(world_texel, world_coord.layer));
|
||||
vec2 world_uv = wrapped_uv * world_coord.scale + world_coord.offset;
|
||||
vec4 world_col = textureLod(atlas_tx, vec3(world_uv, world_coord.layer), 0.0);
|
||||
col.rgb = mix(world_col.rgb, col.rgb, col.a);
|
||||
}
|
||||
|
||||
imageStore(atlas_dst_mip_img, ivec3(texel, write_coord.layer), col);
|
||||
imageStore(atlas_img, ivec3(texel, write_coord.layer), col);
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ GPU_SHADER_CREATE_INFO(eevee_reflection_probe_remap)
|
||||
.push_constant(Type::IVEC4, "world_coord_packed")
|
||||
.push_constant(Type::INT, "mip_level")
|
||||
.sampler(0, ImageType::FLOAT_CUBE, "cubemap_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_dst_mip_img")
|
||||
.image(1, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "atlas_src_mip_img")
|
||||
.sampler(1, ImageType::FLOAT_2D_ARRAY, "atlas_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "atlas_img")
|
||||
.compute_source("eevee_reflection_probe_remap_comp.glsl")
|
||||
.additional_info("eevee_shared")
|
||||
.do_static_compilation(true);
|
||||
|
Loading…
Reference in New Issue
Block a user