diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index 1a89fab6f67..04720d89ec9 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -775,9 +775,6 @@ class RENDER_PT_eevee_next_sampling_shadows(RenderButtonsPanel, Panel): col.prop(props, "shadow_ray_count", text="Rays") col.prop(props, "shadow_step_count", text="Steps") - col = layout.column() - col.prop(props, "shadow_normal_bias", text="Normal Bias") - col = layout.column(align=False, heading="Volume Shadows") row = col.row(align=True) sub = row.row(align=True) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 14dc663c308..53ebc24c2c8 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -2541,12 +2541,11 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } - if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "float", "shadow_normal_bias")) { + if (!DNA_struct_member_exists(fd->filesdna, "SceneEEVEE", "int", "shadow_step_count")) { SceneEEVEE default_scene_eevee = *DNA_struct_default_get(SceneEEVEE); LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { scene->eevee.shadow_ray_count = default_scene_eevee.shadow_ray_count; scene->eevee.shadow_step_count = default_scene_eevee.shadow_step_count; - scene->eevee.shadow_normal_bias = default_scene_eevee.shadow_normal_bias; } } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index e5fa741f96a..41bd003c443 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -1237,6 +1237,11 @@ struct ShadowTileMapData { float half_size; /** Offset in local space to the tilemap center in world units. Used for directional winmat. */ float2 center_offset; + /** Filter radius of the light in pixels. */ + float filter_radius; + float _pad0; + float _pad1; + float _pad2; }; BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16) @@ -1254,8 +1259,8 @@ struct ShadowRenderView { float clip_distance_inv; /* Viewport to submit the geometry of this tilemap view to. */ uint viewport_index; - - uint _pad0; + /* Filter radius for this view. */ + float filter_radius; uint _pad1; }; BLI_STATIC_ASSERT_ALIGN(ShadowRenderView, 16) @@ -1474,10 +1479,10 @@ struct ShadowSceneData { int ray_count; /* Number of shadow samples to take for each shadow ray. */ int step_count; - /* Bias the shading point by using the normal to avoid self intersection. */ - float normal_bias; /* Ratio between tile-map pixel world "radius" and film pixel world "radius". */ float tilemap_projection_ratio; + + float _pad0; }; BLI_STATIC_ASSERT_ALIGN(ShadowSceneData, 16) diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index a9bb7396f6d..37377580691 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -30,15 +30,19 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_, int2 origin_offset, int clipmap_level, float lod_bias_, + float filter_radius_, eShadowProjectionType projection_type_) { - if (projection_type != projection_type_ || (level != clipmap_level)) { + if ((projection_type != projection_type_) || (level != clipmap_level) || + (filter_radius != filter_radius_)) + { set_dirty(); } projection_type = projection_type_; level = clipmap_level; light_type = eLightType::LIGHT_SUN; is_area_side = false; + filter_radius = filter_radius_; if (grid_shift == int2(0)) { /* Only replace shift if it is not already dirty. */ @@ -78,6 +82,7 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_, float side_, float shift, eCubeFace face, + float filter_radius_, float lod_bias_) { if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face)) { @@ -90,10 +95,13 @@ void ShadowTileMap::sync_cubeface(eLightType light_type_, light_type = light_type_; is_area_side = is_area_light(light_type) && (face != eCubeFace::Z_NEG); - if ((clip_near != near_) || (clip_far != far_) || (half_size != side_)) { + if ((clip_near != near_) || (filter_radius != filter_radius_) || (clip_far != far_) || + (half_size != side_)) + { set_dirty(); } + filter_radius = filter_radius_; clip_near = near_; clip_far = far_; area_shift = shift; @@ -377,20 +385,21 @@ void ShadowPunctual::end_sync(Light &light, float lod_bias) tilemaps_.append(tilemap_pool.acquire()); } - tilemaps_[Z_NEG]->sync_cubeface(light.type, obmat_tmp, near, far, side, shift, Z_NEG, lod_bias); + tilemaps_[Z_NEG]->sync_cubeface( + light.type, obmat_tmp, near, far, side, shift, Z_NEG, light.pcf_radius, lod_bias); if (tilemaps_needed_ >= 5) { tilemaps_[X_POS]->sync_cubeface( - light.type, obmat_tmp, near, far, side, shift, X_POS, lod_bias); + light.type, obmat_tmp, near, far, side, shift, X_POS, light.pcf_radius, lod_bias); tilemaps_[X_NEG]->sync_cubeface( - light.type, obmat_tmp, near, far, side, shift, X_NEG, lod_bias); + light.type, obmat_tmp, near, far, side, shift, X_NEG, light.pcf_radius, lod_bias); tilemaps_[Y_POS]->sync_cubeface( - light.type, obmat_tmp, near, far, side, shift, Y_POS, lod_bias); + light.type, obmat_tmp, near, far, side, shift, Y_POS, light.pcf_radius, lod_bias); tilemaps_[Y_NEG]->sync_cubeface( - light.type, obmat_tmp, near, far, side, shift, Y_NEG, lod_bias); + light.type, obmat_tmp, near, far, side, shift, Y_NEG, light.pcf_radius, lod_bias); } if (tilemaps_needed_ == 6) { tilemaps_[Z_POS]->sync_cubeface( - light.type, obmat_tmp, near, far, side, shift, Z_POS, lod_bias); + light.type, obmat_tmp, near, far, side, shift, Z_POS, light.pcf_radius, lod_bias); } light.tilemap_index = tilemap_pool.tilemaps_data.size(); @@ -535,7 +544,8 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera /* Equal spacing between cascades layers since we want uniform shadow density. */ int2 level_offset = origin_offset + shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i); - tilemap->sync_orthographic(object_mat_, level_offset, level, 0.0f, SHADOW_PROJECTION_CASCADE); + tilemap->sync_orthographic( + object_mat_, level_offset, level, 0.0f, light.pcf_radius, SHADOW_PROJECTION_CASCADE); /* Add shadow tile-maps grouped by lights to the GPU buffer. */ shadows_.tilemap_pool.tilemaps_data.append(*tilemap); @@ -602,7 +612,7 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, int2 level_offset = int2(math::round(light_space_camera_position / tile_size)); tilemap->sync_orthographic( - object_mat_, level_offset, level, lod_bias, SHADOW_PROJECTION_CLIPMAP); + object_mat_, level_offset, level, lod_bias, light.pcf_radius, SHADOW_PROJECTION_CLIPMAP); /* Add shadow tile-maps grouped by lights to the GPU buffer. */ shadows_.tilemap_pool.tilemaps_data.append(*tilemap); @@ -770,7 +780,6 @@ void ShadowModule::init() data_.ray_count = clamp_i(inst_.scene->eevee.shadow_ray_count, 1, SHADOW_MAX_RAY); data_.step_count = clamp_i(inst_.scene->eevee.shadow_step_count, 1, SHADOW_MAX_STEP); - data_.normal_bias = max_ff(inst_.scene->eevee.shadow_normal_bias, 0.0f); /* Pool size is in MBytes. */ const size_t pool_byte_size = enabled_ ? scene.eevee.shadow_pool_size * square_i(1024) : 1; diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.hh b/source/blender/draw/engines/eevee_next/eevee_shadow.hh index ab2101367f8..4a7fd165b2c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.hh @@ -101,6 +101,7 @@ struct ShadowTileMap : public ShadowTileMapData { int2 origin_offset, int clipmap_level, float lod_bias_, + float filter_radius, eShadowProjectionType projection_type_); void sync_cubeface(eLightType light_type_, @@ -110,6 +111,7 @@ struct ShadowTileMap : public ShadowTileMapData { float side, float shift, eCubeFace face, + float filter_radius, float lod_bias_); void debug_draw() const; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index 1dc4469ab62..fbb66f5b00d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -14,6 +14,7 @@ void main() DRW_VIEW_FROM_RESOURCE_ID; #ifdef MAT_SHADOW shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index)); + shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius; #endif init_interface(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl index 39d3f1456fc..326b51d3b85 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl @@ -14,6 +14,7 @@ void main() DRW_VIEW_FROM_RESOURCE_ID; #ifdef MAT_SHADOW shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index)); + shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius; #endif init_interface(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl index e7cd94cbc26..0bb7103c9c3 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl @@ -13,6 +13,7 @@ void main() DRW_VIEW_FROM_RESOURCE_ID; #ifdef MAT_SHADOW shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index)); + shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius; #endif init_interface(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_point_cloud_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_point_cloud_vert.glsl index d573bfd93bc..8d5cd94587d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_point_cloud_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_point_cloud_vert.glsl @@ -15,6 +15,7 @@ void main() DRW_VIEW_FROM_RESOURCE_ID; #ifdef MAT_SHADOW shadow_viewport_layer_set(int(drw_view_id), int(render_view_buf[drw_view_id].viewport_index)); + shadow_flat.filter_radius = render_view_buf[drw_view_id].filter_radius; #endif init_interface(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl index c078eb411b3..03e61e06cfe 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl @@ -69,6 +69,11 @@ vec3 light_world_to_local(LightData light, vec3 L) return transform_direction_transposed(light.object_to_world, L); } +vec3 light_world_to_local_point(LightData light, vec3 point) +{ + return transform_point_inversed(light.object_to_world, point); +} + /* From Frostbite PBR Course * Distance based attenuation * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl index 8e16f0409f7..183a96620cb 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_update_comp.glsl @@ -13,9 +13,9 @@ #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) #pragma BLENDER_REQUIRE(draw_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) #pragma BLENDER_REQUIRE(common_intersect_lib.glsl) #pragma BLENDER_REQUIRE(common_aabb_lib.glsl) -#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) vec3 safe_project(mat4 winmat, mat4 viewmat, inout int clipped, vec3 v) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index 1f88c91539a..64d7a2c343d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -89,7 +89,7 @@ void shadow_tag_usage_tilemap_punctual( return; } - vec3 lP = light_world_to_local(light, P - light_position_get(light)); + vec3 lP = light_world_to_local_point(light, P); float dist_to_light = max(length(lP) - radius, 1e-5); if (dist_to_light > light_local_data_get(light).influence_radius_max) { return; @@ -112,7 +112,7 @@ void shadow_tag_usage_tilemap_punctual( lP += vec3(0.0, 0.0, -light_local_data_get(light).shadow_projection_shift); float footprint_ratio = shadow_punctual_footprint_ratio( - light, P, drw_view_is_perspective(), dist_to_cam, tilemap_proj_ratio); + light, lP, drw_view_is_perspective(), dist_to_cam, tilemap_proj_ratio); if (radius == 0) { int face_id = shadow_punctual_face_index_get(lP); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl index facbf20698f..af0a343f7b2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_bounds_comp.glsl @@ -11,9 +11,9 @@ */ #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) -#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) #pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl) #pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) shared int global_min; shared int global_max; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl index f2342bbf78e..73fac997313 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl @@ -105,6 +105,10 @@ void main() /* Setup the view. */ render_view_buf[view_index].viewport_index = viewport_index; + /* Scale by actual radius size (overestimate since scaled by bounding circle). */ + float filter_radius = tilemap_data.filter_radius * M_SQRT2; + /* We need a minimum slope bias even if filter is 0 to avoid some invalid shadowing. */ + render_view_buf[view_index].filter_radius = max(1.0, filter_radius); /* Clipping setup. */ if (tilemap_data.is_area_side) { /* Negative for tagging this case. See shadow_clip_vector_get for explanation. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl index 0a16d16bc4a..f863a932117 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma BLENDER_REQUIRE(common_shape_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_math_vector_lib.glsl) #pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl) /* ---------------------------------------------------------------------- */ @@ -139,7 +140,7 @@ int shadow_directional_level(LightData light, vec3 lP) /* How much a tilemap pixel covers a final image pixel. */ float shadow_punctual_footprint_ratio(LightData light, - vec3 P, + vec3 lP, bool is_perspective, float dist_to_cam, float tilemap_projection_ratio) @@ -149,15 +150,14 @@ float shadow_punctual_footprint_ratio(LightData light, * This gives a good approximation of what LOD to select to get a somewhat uniform shadow map * resolution in screen space. */ - float dist_to_light = distance(P, light_position_get(light)); - float footprint_ratio = dist_to_light; + float dist_to_light = length(lP); + /* Apply resolution ratio. */ + float footprint_ratio = dist_to_light * tilemap_projection_ratio; /* 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. */ if (is_perspective) { footprint_ratio /= dist_to_cam; } - /* Apply resolution ratio. */ - footprint_ratio *= tilemap_projection_ratio; /* Take the frustum padding into account. */ footprint_ratio *= light_local_data_get(light).clip_side / orderedIntBitsToFloat(light.clip_near); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl index 6edc23b7c98..475815954c7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl @@ -405,103 +405,87 @@ SHADOW_MAP_TRACE_FN(ShadowRayPunctual) /* Compute the world space offset of the shading position required for * stochastic percentage closer filtering of shadow-maps. */ -vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3 Ng, vec2 random) +vec3 shadow_pcf_offset( + LightData light, const bool is_directional, vec3 L, vec3 Ng, float texel_radius, vec2 random) { if (light.pcf_radius <= 0.001) { /* Early return. */ return vec3(0.0); } - vec3 L = light_vector_get(light, is_directional, P).L; - if (dot(L, Ng) < 0.001) { - /* Don't apply PCF to almost perpendicular, - * since we can't project the offset to the surface. */ - return vec3(0.0); + /* We choose a random disk distribution because it is rotationally invariant. + * This sames us the trouble of getting the correct orientation for punctual. */ + vec2 disk_sample = sample_disk(random) * (texel_radius * light.pcf_radius); + /* Compute the offset as a disk around the normal. */ + mat3x3 tangent_frame = from_up_axis(Ng); + vec3 pcf_offset = tangent_frame[0] * disk_sample.x + tangent_frame[1] * disk_sample.y; + + if (dot(pcf_offset, L) < 0.0) { + /* Reflect the offset to avoid overshadowing caused by moving the sampling point below another + * polygon behind the shading point. */ + pcf_offset = reflect(pcf_offset, L); } - ShadowSampleParams params; + return pcf_offset; +} + +/** + * Returns the world space radius of a shadow map texel at a given position. + * This is a smooth (not discretized to the LOD transitions) conservative (always above actual + * density) estimate value. + */ +float shadow_texel_radius_at_position(LightData light, const bool is_directional, vec3 P) +{ + vec3 lP = light_world_to_local_point(light, P); + + float scale = 1.0; if (is_directional) { - params = shadow_directional_sample_params_get(shadow_tilemaps_tx, light, P); + LightSunData sun = light_sun_data_get(light); + if (light.type == LIGHT_SUN) { + /* Simplification of `coverage_get(shadow_directional_level_fractional)`. */ + const float narrowing = float(SHADOW_TILEMAP_RES) / (float(SHADOW_TILEMAP_RES) - 1.0001); + scale = length(lP) * narrowing; + scale *= exp2(light.lod_bias); + scale = clamp(scale, float(1 << sun.clipmap_lod_min), float(1 << sun.clipmap_lod_max)); + } + else { + /* Uniform distribution everywhere. No distance scaling. */ + scale = 1.0 / float(1 << sun.clipmap_lod_min); + } } else { - params = shadow_punctual_sample_params_get(light, P); - } - ShadowSamplingTile tile = shadow_tile_data_get(shadow_tilemaps_tx, params); - if (!tile.is_valid) { - return vec3(0.0); + /* FIXME: The returned value seems quite broken as it increases drastically near the view + * position. */ + scale = shadow_punctual_footprint_ratio(light, + lP, + drw_view_is_perspective(), + distance(P, drw_view_position()), + uniform_buf.shadow.tilemap_projection_ratio); + /* This gives the size of pixels at Z = 1. */ + scale *= exp2(light.lod_bias); + scale = clamp(scale, float(1 << 0), float(1 << SHADOW_TILEMAP_LOD)); + /* Now scale by distance to the light. */ + scale *= length(lP); } + /* Footprint of a tilemap at unit distance from the camera. */ + const float texel_footprint = 2.0 * M_SQRT2 / SHADOW_MAP_MAX_RES; + return texel_footprint * scale; +} - /* Compute the shadow-map tangent-bitangent matrix. */ - - float uv_offset = 1.0 / float(SHADOW_MAP_MAX_RES); - vec3 TP, BP; - if (is_directional) { - TP = shadow_directional_reconstruct_position( - params, light, params.uv + vec3(uv_offset, 0.0, 0.0)); - BP = shadow_directional_reconstruct_position( - params, light, params.uv + vec3(0.0, uv_offset, 0.0)); - } - else { - mat4 wininv = shadow_punctual_projection_perspective_inverse(light); - TP = shadow_punctual_reconstruct_position( - params, wininv, light, params.uv + vec3(uv_offset, 0.0, 0.0)); - BP = shadow_punctual_reconstruct_position( - params, wininv, light, params.uv + vec3(0.0, uv_offset, 0.0)); - } - - /* TODO: Use a mat2x3 (Currently not supported by the Metal backend). */ - mat3 TBN = mat3(TP - P, BP - P, Ng); - - /* Compute the actual offset. */ - - vec2 pcf_offset = random * 2.0 - 1.0; - pcf_offset *= light.pcf_radius; - - /* Scale the offset based on shadow LOD. */ - if (is_directional) { - vec3 lP = light_world_to_local(light, P); - float level = shadow_directional_level_fractional(light, lP - light_position_get(light)); - float pcf_scale = mix(0.5, 1.0, fract(level)); - pcf_offset *= pcf_scale; - } - else { - bool is_perspective = drw_view_is_perspective(); - float dist_to_cam = distance(P, drw_view_position()); - float footprint_ratio = shadow_punctual_footprint_ratio( - light, P, is_perspective, dist_to_cam, uniform_buf.shadow.tilemap_projection_ratio); - float lod = -log2(footprint_ratio) + light.lod_bias; - lod = clamp(lod, 0.0, float(SHADOW_TILEMAP_LOD)); - float pcf_scale = exp2(lod); - pcf_offset *= pcf_scale; - } - - vec3 ws_offset = TBN * vec3(pcf_offset, 0.0); - vec3 offset_P = P + ws_offset; - - /* Project the offset position into the surface */ - -#ifdef GPU_NVIDIA - /* Workaround for a bug in the Nvidia shader compiler. - * If we don't compute L here again, it breaks shadows on reflection probes. */ - L = light_vector_get(light, is_directional, P).L; -#endif - - if (abs(dot(Ng, L)) > 0.999) { - return ws_offset; - } - - offset_P = line_plane_intersect(offset_P, L, P, Ng); - ws_offset = offset_P - P; - - if (dot(ws_offset, L) < 0.0) { - /* Project the offset position into the perpendicular plane, since it's closer to the light - * (avoids overshadowing at geometry angles). */ - vec3 perpendicular_plane_normal = cross(Ng, normalize(cross(Ng, L))); - offset_P = line_plane_intersect(offset_P, L, P, perpendicular_plane_normal); - ws_offset = offset_P - P; - } - - return ws_offset; +/** + * Compute the amount of offset to add to the shading point in the normal direction to avoid self + * shadowing caused by aliasing artifacts. This is on top of the slope bias computed in the shadow + * render shader to avoid aliasing issues of other polygons. The slope bias only fixes the self + * shadowing from the current polygon, which is not enough in cases with adjacent polygons with + * very different slopes. + */ +float shadow_normal_offset(float texel_radius, vec3 Ng, vec3 L) +{ + /* Attenuate depending on light angle. */ + /* TODO: Should we take the light shape into consideration? */ + float cos_theta = abs(dot(Ng, L)); + float sin_theta = sqrt(saturate(1.0 - square(cos_theta))); + return texel_radius * sin_theta; } /** @@ -526,28 +510,27 @@ ShadowEvalResult shadow_eval(LightData light, vec3 blue_noise_3d = utility_tx_fetch(utility_tx, pixel, UTIL_BLUE_NOISE_LAYER).rgb; vec3 random_shadow_3d = blue_noise_3d + sampling_rng_3D_get(SAMPLING_SHADOW_U); vec2 random_pcf_2d = fract(blue_noise_3d.xy + sampling_rng_2D_get(SAMPLING_SHADOW_X)); - float normal_offset = uniform_buf.shadow.normal_bias; #else /* Case of surfel light eval. */ vec3 random_shadow_3d = vec3(0.5); vec2 random_pcf_2d = vec2(0.0); - /* TODO(fclem): Parameter on irradiance volumes? */ - float normal_offset = 0.02; #endif - P += shadow_pcf_offset(light, is_directional, P, Ng, random_pcf_2d); + /* Shadow map texel radius at the receiver position. */ + float texel_radius = shadow_texel_radius_at_position(light, is_directional, P); + + P += shadow_pcf_offset(light, is_directional, L, Ng, texel_radius, random_pcf_2d); /* We want to bias inside the object for transmission to go through the object itself. - * But doing so split the shadow in two different directions at the horizon. Also this + * But doing so splits the shadow in two different directions at the horizon. Also this * doesn't fix the the aliasing issue. So we reflect the normal so that it always go towards * the light. */ vec3 N_bias = is_transmission ? reflect(Ng, L) : Ng; - /* Avoid self intersection. */ + /* Avoid self intersection with respect to numerical precision. */ P = offset_ray(P, N_bias); /* The above offset isn't enough in most situation. Still add a bigger bias. */ - /* TODO(fclem): Scale based on depth. */ - P += N_bias * normal_offset; + P += N_bias * shadow_normal_offset(texel_radius, Ng, L); vec3 lP = is_directional ? light_world_to_local(light, P) : light_world_to_local(light, P - light_position_get(light)); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl index 8e4b8c0ace7..8cbd3fbdc26 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl @@ -24,7 +24,12 @@ vec4 closure_to_rgba(Closure cl) void main() { - float f_depth = gl_FragCoord.z + fwidth(gl_FragCoord.z); + float f_depth = gl_FragCoord.z; + /* Slope bias. + * Note that we always need a minimum slope bias of 1 pixel to avoid slanted surfaces aliasing + * onto facing surfaces. + * IMPORTANT: `fwidth` needs to be inside uniform control flow. */ + f_depth += fwidth(f_depth) * shadow_flat.filter_radius; #ifdef SHADOW_UPDATE_TBDR /* We need to write to `gl_FragDepth` un-conditionally. So we cannot early exit or use discard. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index 6b1241c1fcc..2cfd7c996d5 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -234,11 +234,15 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_shadow_atomic_iface, "shadow_iface") GPU_SHADER_INTERFACE_INFO(eevee_surf_shadow_clipping_iface, "shadow_clip") .smooth(Type::VEC3, "vector"); +GPU_SHADER_INTERFACE_INFO(eevee_surf_shadow_flat_iface, "shadow_flat") + .flat(Type::FLOAT, "filter_radius"); + GPU_SHADER_CREATE_INFO(eevee_surf_shadow) .define("DRW_VIEW_LEN", STRINGIFY(SHADOW_VIEW_MAX)) .define("MAT_SHADOW") .builtins(BuiltinBits::VIEWPORT_INDEX) .vertex_out(eevee_surf_shadow_clipping_iface) + .vertex_out(eevee_surf_shadow_flat_iface) .storage_buf(SHADOW_RENDER_VIEW_BUF_SLOT, Qualifier::READ, "ShadowRenderView", diff --git a/source/blender/draw/tests/eevee_test.cc b/source/blender/draw/tests/eevee_test.cc index 1051a8e63b4..13829fe851c 100644 --- a/source/blender/draw/tests/eevee_test.cc +++ b/source/blender/draw/tests/eevee_test.cc @@ -234,12 +234,13 @@ static void test_eevee_shadow_tag_update() { ShadowTileMap tilemap(0 * SHADOW_TILEDATA_PER_TILEMAP); tilemap.sync_cubeface( - LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f); + LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f, 0.0f); tilemaps_data.append(tilemap); } { ShadowTileMap tilemap(1 * SHADOW_TILEDATA_PER_TILEMAP); - tilemap.sync_orthographic(float4x4::identity(), int2(0), 1, 0.0f, SHADOW_PROJECTION_CLIPMAP); + tilemap.sync_orthographic( + float4x4::identity(), int2(0), 1, 0.0f, 0.0f, SHADOW_PROJECTION_CLIPMAP); tilemaps_data.append(tilemap); } @@ -1543,7 +1544,7 @@ static void test_eevee_shadow_page_mask_ex(int max_view_per_tilemap) { ShadowTileMap tilemap(0); tilemap.sync_cubeface( - LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f); + LIGHT_OMNI_SPHERE, float4x4::identity(), 0.01f, 1.0f, 0.01f, 0.0f, Z_NEG, 0.0f, 0.0f); tilemaps_data.append(tilemap); } diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 35eccb3254e..4b388dc57f1 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -236,7 +236,6 @@ .shadow_cascade_size = 1024, \ .shadow_ray_count = 1, \ .shadow_step_count = 6, \ - .shadow_normal_bias = 0.02f, \ \ .ray_tracing_method = RAYTRACE_EEVEE_METHOD_SCREEN, \ \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f1940b309de..2fb0a1c5c61 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1873,7 +1873,7 @@ typedef struct SceneEEVEE { int shadow_pool_size; int shadow_ray_count; int shadow_step_count; - float shadow_normal_bias; + char _pad[4]; float clamp_surface_direct; float clamp_surface_indirect; diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 02df23d16d9..c2bc78e29a1 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -8419,13 +8419,6 @@ static void rna_def_scene_eevee(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); - prop = RNA_def_property(srna, "shadow_normal_bias", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_range(prop, 0.0f, FLT_MAX); - RNA_def_property_ui_range(prop, 0.001f, 0.1f, 0.001, 3); - RNA_def_property_ui_text(prop, "Shadow Normal Bias", "Move shadows along their normal"); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); - prop = RNA_def_property(srna, "use_shadow_high_bitdepth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", SCE_EEVEE_SHADOW_HIGH_BITDEPTH); RNA_def_property_ui_text(prop, "High Bit Depth", "Use 32-bit shadows");