Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2021-12-06 01:08:46 +0300
committerClément Foucault <foucault.clem@gmail.com>2021-12-06 01:08:46 +0300
commitb23d9519d0cc0b11d1a6fc590da35b75206e40eb (patch)
treefd0711d80f853a9c273e1ad0d938470d59c91234 /source/blender/draw/engines
parent7303a453aa7ee3c6397057ffa4bb61c60a782e06 (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')
-rw-r--r--source/blender/draw/engines/eevee/eevee_light.cc2
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadow.cc23
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadow.hh4
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_lib.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_tag_comp.glsl47
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_shadow_tilemap_visibility_comp.glsl43
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;