diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2021-12-06 01:08:46 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2021-12-06 01:08:46 +0300 |
commit | b23d9519d0cc0b11d1a6fc590da35b75206e40eb (patch) | |
tree | fd0711d80f853a9c273e1ad0d938470d59c91234 /source/blender/draw/engines | |
parent | 7303a453aa7ee3c6397057ffa4bb61c60a782e06 (diff) |
EEVEE: Shadow: Make punctual LOD selection based on pixel density
Until now the LOD selection was based on distance from camera.
Now it is based on receiver distance ratio. We compute the world
size of one view pixel along with the world size of one shadow texel.
By knowing one point distance to the light or to the view, we can
compute the pixel density ratio and deduce the corresponding LOD.
We use this to compute the min LOD during the visibility selection phase
and the "mean" LOD for usage tagging by BBoxes.
The tagging LOD is a crude approximation as it only uses the BBox
center.
Diffstat (limited to 'source/blender/draw/engines')
6 files changed, 98 insertions, 28 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_light.cc b/source/blender/draw/engines/eevee/eevee_light.cc index 2f6a4e32ca4..80e6bf8d5e9 100644 --- a/source/blender/draw/engines/eevee/eevee_light.cc +++ b/source/blender/draw/engines/eevee/eevee_light.cc @@ -446,7 +446,7 @@ void LightModule::set_view(const DRWView *view, const ivec2 extent, bool enable_ DRW_view_set_active(view); DRW_draw_pass(culling_ps_); - inst_.shadows.update_visible(view); + inst_.shadows.update_visible(view, extent); } void LightModule::debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz) diff --git a/source/blender/draw/engines/eevee/eevee_shadow.cc b/source/blender/draw/engines/eevee/eevee_shadow.cc index 5c70d52aac8..5ae2dcc077b 100644 --- a/source/blender/draw/engines/eevee/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee/eevee_shadow.cc @@ -565,6 +565,8 @@ void ShadowModule::init(void) } #endif + tilemap_pixel_radius_ = M_SQRT2 * 2.0f / (SHADOW_TILEMAP_RES * shadow_page_size_); + debug_data_.type = inst_.debug_mode; } @@ -713,6 +715,8 @@ void ShadowModule::end_sync(void) DRWShadingGroup *grp = DRW_shgroup_create(sh, tilemap_visibility_ps_); DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1); + DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1); if (tilemaps_len > 0) { DRW_shgroup_call_compute(grp, 1, 1, tilemaps_len); DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); @@ -729,6 +733,8 @@ void ShadowModule::end_sync(void) DRW_shgroup_vertex_buffer(grp, "aabb_buf", receivers_aabbs); DRW_shgroup_vertex_buffer(grp, "tilemaps_buf", tilemap_allocator.tilemaps_data); DRW_shgroup_uniform_image(grp, "tilemaps_img", tilemap_allocator.tilemap_tx); + DRW_shgroup_uniform_float(grp, "tilemap_pixel_radius", &tilemap_pixel_radius_, 1); + DRW_shgroup_uniform_float(grp, "screen_pixel_radius_inv", &screen_pixel_radius_inv_, 1); DRW_shgroup_uniform_int_copy(grp, "aabb_len", aabb_len); if (tilemaps_len > 0 && aabb_len > 0) { uint group_len = divide_ceil_u(aabb_len, SHADOW_AABB_TAG_GROUP_SIZE); @@ -900,7 +906,7 @@ void ShadowModule::debug_end_sync(void) } /* Update all shadow regions visible inside the view. */ -void ShadowModule::update_visible(const DRWView *view) +void ShadowModule::update_visible(const DRWView *view, const ivec2 extent) { #if 0 /* TODO */ bool force_update = false; @@ -915,6 +921,21 @@ void ShadowModule::update_visible(const DRWView *view) DRW_view_set_active(view); + float4x4 wininv; + DRW_view_winmat_get(view, wininv.values, true); + + float min_dim = float(min_ii(extent.x, extent.y)); + float3 p0 = float3(-1.0f, -1.0f, 0.0f); + float3 p1 = float3(min_dim / extent.x, min_dim / extent.y, 0.0f) * 2.0f - 1.0f; + mul_project_m4_v3(wininv.values, p0); + mul_project_m4_v3(wininv.values, p1); + /* Compute radius at unit plane from the camera. */ + if (DRW_view_is_persp_get(view)) { + p0 = p0 / p0.z; + p1 = p1 / p1.z; + } + screen_pixel_radius_inv_ = min_dim / float3::distance(p0, p1); + #ifdef DEBUG static bool valid = false; static float4x4 viewmat_freezed; diff --git a/source/blender/draw/engines/eevee/eevee_shadow.hh b/source/blender/draw/engines/eevee/eevee_shadow.hh index 42c4c156160..71aa7075a01 100644 --- a/source/blender/draw/engines/eevee/eevee_shadow.hh +++ b/source/blender/draw/engines/eevee/eevee_shadow.hh @@ -471,6 +471,8 @@ class ShadowModule { DRWCallBuffer *receivers_non_opaque_; bool do_tilemap_setup_ = true; + float tilemap_pixel_radius_; + float screen_pixel_radius_inv_; /** \} */ @@ -551,7 +553,7 @@ class ShadowModule { bool is_alpha_blend); void end_sync(void); - void update_visible(const DRWView *view); + void update_visible(const DRWView *view, const ivec2 extent); void debug_end_sync(void); void debug_draw(GPUFrameBuffer *view_fb, HiZBuffer &hiz); diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl index 3ced53a1e10..ed8870443e8 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl @@ -154,13 +154,6 @@ int shadow_directional_clipmap_level(ShadowData shadow, float distance_to_camera return clamp(clipmap_lod, shadow.clipmap_lod_min, shadow.clipmap_lod_max); } -int shadow_punctual_lod_level(float distance_to_camera) -{ - /* FIXME(fclem): Does not work great with orthographic projection. */ - /* TODO use pixel density. */ - return int(log2(distance_to_camera)); -} - /** \} */ /* ---------------------------------------------------------------------- */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl index e3b72df5fd4..72457880b73 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl @@ -35,6 +35,8 @@ layout(std430, binding = 1) readonly buffer aabb_buf layout(r32ui) restrict uniform uimage2D tilemaps_img; uniform int aabb_len; +uniform float tilemap_pixel_radius; +uniform float screen_pixel_radius_inv; vec3 safe_project(ShadowTileMapData tilemap, inout int clipped, vec3 v) { @@ -82,32 +84,51 @@ void main() } #endif + int lod_min = 0; + int lod_max = 0; +#ifdef TAG_USAGE + if (tilemap.is_cubeface) { + /* TODO(fclem): This is imprecise as we only evaluate one point. + * Evaluate more point to get a range? but which ones? */ + vec3 nearest_receiver = (aabb.min + aabb.max) * 0.5; + float len = distance(nearest_receiver, frustum.corners[0]); + /* How much a shadow map pixel covers a final image pixel. */ + float footprint_ratio = len * (tilemap_pixel_radius * screen_pixel_radius_inv); + /* Project the radius to the screen. 1 unit away from the camera the same way + * pixel_world_radius_inv was computed. Not needed in orthographic mode. */ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) { + footprint_ratio /= distance(nearest_receiver, cameraPos); + } + + lod_min = lod_max = int(ceil(-log2(footprint_ratio))); + +# if 0 /* DEBUG */ + vec4 green = vec4(0, 1, 0, 1); + vec4 yellow = vec4(1, 1, 0, 1); + vec4 red = vec4(1, 0, 0, 1); + float dist_fac = (is_persp) ? distance(nearest_receiver, cameraPos) : 1.0; + drw_debug_point(nearest_receiver, 128.0 * dist_fac / screen_pixel_radius_inv, green); + drw_debug_point(nearest_receiver, len * tilemap_pixel_radius * 128.0, yellow); +# endif + } + lod_max = clamp(lod_max, 0, SHADOW_TILEMAP_LOD); + lod_min = clamp(lod_min, 0, SHADOW_TILEMAP_LOD); +#endif + #ifdef TAG_UPDATE // drw_debug(aabb, vec4(0, 1, 0, 1)); #else /* TAG_USAGE */ // drw_debug(aabb, vec4(1, 1, 0, 1)); #endif - int lod_min = tilemap.is_cubeface ? SHADOW_TILEMAP_LOD : 0; - int lod_max = 0; int clipped = 0; /* TODO(fclem) project bbox instead of AABB. */ /* NDC space post projection [-1..1] (unclamped). */ AABB aabb_ndc = init_min_max(); for (int v = 0; v < 8; v++) { merge(aabb_ndc, safe_project(tilemap, clipped, box.corners[v])); - -#ifdef TAG_USAGE - if (tilemap.is_cubeface) { - /* FIXME(fclem) this will fail if camera is inside the box. */ - int lod_visible = shadow_punctual_lod_level(distance(cameraPos, box.corners[v])); - lod_min = min(lod_min, lod_visible); - lod_max = max(lod_max, lod_visible); - } -#endif } - lod_max = clamp(lod_max, 0, SHADOW_TILEMAP_LOD); - lod_min = clamp(lod_min, 0, SHADOW_TILEMAP_LOD); #ifdef TAG_UPDATE /* Update tag all LODs. */ diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl index 12a10400632..34d6b43ea05 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl @@ -18,6 +18,9 @@ layout(std430, binding = 0) readonly buffer tilemaps_buf ShadowTileMapData tilemaps[]; }; +uniform float tilemap_pixel_radius; +uniform float screen_pixel_radius_inv; + layout(r32ui) restrict uniform uimage2D tilemaps_img; void main() @@ -47,12 +50,42 @@ void main() if (is_intersecting) { /* Test minimum receiver distance and compute min and max visible LOD. */ float len; - vec3 tile_center = shape.corners[1] + shape.corners[3] * 0.5; - vec3 corner_vec = normalize_len(tile_center - shape.corners[0], len); - float projection_len = dot(corner_vec, cameraPos - shape.corners[0]); - vec3 nearest_receiver = corner_vec * min(len, projection_len); + vec3 tile_center = (shape.corners[1] + shape.corners[3]) * 0.5; + vec3 tile_center_dir = normalize_len(tile_center - shape.corners[0], len); + /* Project the tile center to the frustum and compare the shadow texel density at this + * position since this is where the density ratio will be the lowest (meanning the highest + * LOD). NOTE: There is some inacuracy because we only project one point instead of + * projecting each individual pixels. */ + for (int p = 0; p < 6; p++) { + float facing = dot(tile_center_dir, -frustum_planes[p].xyz); + float d = line_plane_intersect_dist(shape.corners[0], tile_center_dir, frustum_planes[p]); + if (d > 0.0 && facing > 0.0) { + len = min(d, len); + } + } + vec3 nearest_receiver = shape.corners[0] + tile_center_dir * len; + /* How much a shadow map pixel covers a final image pixel. */ + float footprint_ratio = len * (tilemap_pixel_radius * screen_pixel_radius_inv); + /* Project the radius to the screen. 1 unit away from the camera the same way + * pixel_world_radius_inv was computed. Not needed in orthographic mode. */ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + if (is_persp) { + footprint_ratio /= distance(nearest_receiver, cameraPos); + } + +#if 0 /* DEBUG */ + if (gl_GlobalInvocationID.z == 0u) { + vec4 green = vec4(0, 1, 0, 1); + vec4 yellow = vec4(1, 1, 0, 1); + vec4 red = vec4(1, 0, 0, 1); + float dist_fac = (is_persp) ? distance(nearest_receiver, cameraPos) : 1.0; + drw_debug_point(nearest_receiver, 128.0 * dist_fac / screen_pixel_radius_inv, green); + drw_debug_point(shape.corners[0] + tile_center_dir, tilemap_pixel_radius * 128.0, red); + drw_debug_point(nearest_receiver, len * tilemap_pixel_radius * 128.0, yellow); + } +#endif - lod_visible_min = shadow_punctual_lod_level(distance(nearest_receiver, cameraPos)); + lod_visible_min = int(ceil(-log2(footprint_ratio))); /* FIXME(fclem): This should be computed using the farthest intersection with the view. */ lod_visible_max = SHADOW_TILEMAP_LOD; |