Merge pull request #51025 from reduz/fix-directional-shadow-bias

Fix directional shadow bias
This commit is contained in:
Rémi Verschelde 2021-08-13 16:45:49 +02:00 committed by GitHub
commit e499758a77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 20 additions and 103 deletions

View File

@ -36,7 +36,6 @@
<member name="directional_shadow_split_3" type="float" setter="set_param" getter="get_param" default="0.5"> <member name="directional_shadow_split_3" type="float" setter="set_param" getter="get_param" default="0.5">
The distance from shadow split 2 to split 3. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [code]SHADOW_PARALLEL_4_SPLITS[/code]. The distance from shadow split 2 to split 3. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [code]SHADOW_PARALLEL_4_SPLITS[/code].
</member> </member>
<member name="shadow_bias" type="float" setter="set_param" getter="get_param" override="true" default="0.05" />
<member name="shadow_normal_bias" type="float" setter="set_param" getter="get_param" override="true" default="1.0" /> <member name="shadow_normal_bias" type="float" setter="set_param" getter="get_param" override="true" default="1.0" />
<member name="use_in_sky_only" type="bool" setter="set_sky_only" getter="is_sky_only" default="false"> <member name="use_in_sky_only" type="bool" setter="set_sky_only" getter="is_sky_only" default="false">
If [code]true[/code], this [DirectionalLight3D] will not be used for anything except sky shaders. Use this for lights that impact your sky shader that you may want to hide from affecting the rest of the scene. For example, you may want to enable this when the sun in your sky shader falls below the horizon. If [code]true[/code], this [DirectionalLight3D] will not be used for anything except sky shaders. Use this for lights that impact your sky shader that you may want to hide from affecting the rest of the scene. For example, you may want to enable this when the sun in your sky shader falls below the horizon.

View File

@ -13,8 +13,7 @@
<methods> <methods>
</methods> </methods>
<members> <members>
<member name="shadow_bias" type="float" setter="set_param" getter="get_param" override="true" default="0.02" /> <member name="shadow_bias" type="float" setter="set_param" getter="get_param" override="true" default="0.03" />
<member name="shadow_normal_bias" type="float" setter="set_param" getter="get_param" override="true" default="1.0" />
<member name="spot_angle" type="float" setter="set_param" getter="get_param" default="45.0"> <member name="spot_angle" type="float" setter="set_param" getter="get_param" default="45.0">
The spotlight's angle in degrees. The spotlight's angle in degrees.
</member> </member>

View File

@ -212,6 +212,10 @@ void Light3D::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NONE; property.usage = PROPERTY_USAGE_NONE;
} }
if (get_light_type() == RS::LIGHT_SPOT && property.name == "shadow_normal_bias") {
property.usage = PROPERTY_USAGE_NONE;
}
if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") { if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") {
property.usage = PROPERTY_USAGE_NONE; property.usage = PROPERTY_USAGE_NONE;
} }
@ -340,7 +344,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) {
set_param(PARAM_SHADOW_FADE_START, 0.8); set_param(PARAM_SHADOW_FADE_START, 0.8);
set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.0); set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.0);
set_param(PARAM_SHADOW_BLUR, 1.0); set_param(PARAM_SHADOW_BLUR, 1.0);
set_param(PARAM_SHADOW_BIAS, 0.02); set_param(PARAM_SHADOW_BIAS, 0.03);
set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0);
set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05);
set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1); set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1);
@ -422,7 +426,8 @@ DirectionalLight3D::DirectionalLight3D() :
set_param(PARAM_SHADOW_FADE_START, 0.8); set_param(PARAM_SHADOW_FADE_START, 0.8);
// Increase the default shadow bias to better suit most scenes. // Increase the default shadow bias to better suit most scenes.
// Leave normal bias untouched as it doesn't benefit DirectionalLight3D as much as OmniLight3D. // Leave normal bias untouched as it doesn't benefit DirectionalLight3D as much as OmniLight3D.
set_param(PARAM_SHADOW_BIAS, 0.05); set_param(PARAM_SHADOW_BIAS, 0.1);
set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0);
set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS);
blend_splits = false; blend_splits = false;
} }

View File

@ -2777,7 +2777,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
CameraMatrix shadow_mtx = rectm * bias * matrix * modelview; CameraMatrix shadow_mtx = rectm * bias * matrix * modelview;
light_data.shadow_split_offsets[j] = split; light_data.shadow_split_offsets[j] = split;
float bias_scale = li->shadow_transform[j].bias_scale; float bias_scale = li->shadow_transform[j].bias_scale;
light_data.shadow_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) * bias_scale; light_data.shadow_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) / 100.0 * bias_scale;
light_data.shadow_normal_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * li->shadow_transform[j].shadow_texel_size; light_data.shadow_normal_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * li->shadow_transform[j].shadow_texel_size;
light_data.shadow_transmittance_bias[j] = storage->light_get_transmittance_bias(base) * bias_scale; light_data.shadow_transmittance_bias[j] = storage->light_get_transmittance_bias(base) * bias_scale;
light_data.shadow_z_range[j] = li->shadow_transform[j].farplane; light_data.shadow_z_range[j] = li->shadow_transform[j].farplane;
@ -2948,14 +2948,10 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
light_data.shadow_enabled = true; light_data.shadow_enabled = true;
if (type == RS::LIGHT_SPOT) { if (type == RS::LIGHT_SPOT) {
light_data.shadow_bias = (storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) * radius / 10.0); light_data.shadow_bias = (storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) / 100.0);
float shadow_texel_size = Math::tan(Math::deg2rad(spot_angle)) * radius * 2.0;
shadow_texel_size *= light_instance_get_shadow_texel_size(li->self, p_shadow_atlas);
light_data.shadow_normal_bias = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * shadow_texel_size;
} else { //omni } else { //omni
light_data.shadow_bias = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) * radius / 10.0; light_data.shadow_bias = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) / 100.0;
float shadow_texel_size = light_instance_get_shadow_texel_size(li->self, p_shadow_atlas); float shadow_texel_size = light_instance_get_shadow_texel_size(li->self, p_shadow_atlas);
light_data.shadow_normal_bias = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * shadow_texel_size * 2.0; // applied in -1 .. 1 space light_data.shadow_normal_bias = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * shadow_texel_size * 2.0; // applied in -1 .. 1 space
} }

View File

@ -410,14 +410,8 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
vec4 v = vec4(vertex, 1.0); vec4 v = vec4(vertex, 1.0);
vec4 splane = (omni_lights.data[idx].shadow_matrix * v); vec4 splane = (omni_lights.data[idx].shadow_matrix * v);
float shadow_len = length(splane.xyz); //need to remember shadow len from here
{ float shadow_len = length(splane.xyz); //need to remember shadow len from here
vec3 nofs = normal_interp * omni_lights.data[idx].shadow_normal_bias / omni_lights.data[idx].inv_radius;
nofs *= (1.0 - max(0.0, dot(normalize(light_rel_vec), normalize(normal_interp))));
v.xyz += nofs;
splane = (omni_lights.data[idx].shadow_matrix * v);
}
float shadow; float shadow;
@ -526,7 +520,8 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
splane.xy /= splane.z; splane.xy /= splane.z;
splane.xy = splane.xy * 0.5 + 0.5; splane.xy = splane.xy * 0.5 + 0.5;
splane.z = (shadow_len - omni_lights.data[idx].shadow_bias) * omni_lights.data[idx].inv_radius; splane.z = shadow_len * omni_lights.data[idx].inv_radius;
splane.z -= omni_lights.data[idx].shadow_bias;
splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw;
splane.w = 1.0; //needed? i think it should be 1 already splane.w = 1.0; //needed? i think it should be 1 already
shadow = sample_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, splane); shadow = sample_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, splane);
@ -702,27 +697,17 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
//there is a shadowmap //there is a shadowmap
vec4 v = vec4(vertex, 1.0); vec4 v = vec4(vertex, 1.0);
v.xyz -= spot_dir * spot_lights.data[idx].shadow_bias;
float z_norm = dot(spot_dir, -light_rel_vec) * spot_lights.data[idx].inv_radius;
float depth_bias_scale = 1.0 / (max(0.0001, z_norm)); //the closer to the light origin, the more you have to offset to reach 1px in the map
vec3 normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(spot_dir, -normalize(normal_interp)))) * spot_lights.data[idx].shadow_normal_bias * depth_bias_scale;
normal_bias -= spot_dir * dot(spot_dir, normal_bias); //only XY, no Z
v.xyz += normal_bias;
//adjust with bias
z_norm = dot(spot_dir, v.xyz - spot_lights.data[idx].position) * spot_lights.data[idx].inv_radius;
float shadow; float shadow;
vec4 splane = (spot_lights.data[idx].shadow_matrix * v); vec4 splane = (spot_lights.data[idx].shadow_matrix * v);
splane /= splane.w; splane /= splane.w;
splane.z -= spot_lights.data[idx].shadow_bias;
if (sc_use_light_soft_shadows && spot_lights.data[idx].soft_shadow_size > 0.0) { if (sc_use_light_soft_shadows && spot_lights.data[idx].soft_shadow_size > 0.0) {
//soft shadow //soft shadow
//find blocker //find blocker
float z_norm = dot(spot_dir, -light_rel_vec) * spot_lights.data[idx].inv_radius;
vec2 shadow_uv = splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy; vec2 shadow_uv = splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy;

View File

@ -1955,10 +1955,6 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
bool overlap = RSG::storage->light_directional_get_blend_splits(p_instance->base); bool overlap = RSG::storage->light_directional_get_blend_splits(p_instance->base);
real_t first_radius = 0.0;
real_t min_distance_bias_scale = distances[1];
cull.shadow_count = p_shadow_index + 1; cull.shadow_count = p_shadow_index + 1;
cull.shadows[p_shadow_index].cascade_count = splits; cull.shadows[p_shadow_index].cascade_count = splits;
cull.shadows[p_shadow_index].light_instance = light->instance; cull.shadows[p_shadow_index].light_instance = light->instance;
@ -2006,8 +2002,8 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
real_t z_min_cam = 0.f; real_t z_min_cam = 0.f;
//real_t z_max_cam = 0.f; //real_t z_max_cam = 0.f;
real_t bias_scale = 1.0; //real_t bias_scale = 1.0;
real_t aspect_bias_scale = 1.0; //real_t aspect_bias_scale = 1.0;
//used for culling //used for culling
@ -2061,12 +2057,6 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
radius *= texture_size / (texture_size - 2.0); //add a texel by each side radius *= texture_size / (texture_size - 2.0); //add a texel by each side
if (i == 0) {
first_radius = radius;
} else {
bias_scale = radius / first_radius;
}
z_min_cam = z_vec.dot(center) - radius; z_min_cam = z_vec.dot(center) - radius;
{ {
@ -2110,64 +2100,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
// a pre pass will need to be needed to determine the actual z-near to be used // a pre pass will need to be needed to determine the actual z-near to be used
if (pancake_size > 0) {
z_max = z_vec.dot(center) + radius + pancake_size; z_max = z_vec.dot(center) + radius + pancake_size;
}
if (aspect != 1.0) {
// if the aspect is different, then the radius will become larger.
// if this happens, then bias needs to be adjusted too, as depth will increase
// to do this, compare the depth of one that would have resulted from a square frustum
CameraMatrix camera_matrix_square;
if (p_cam_orthogonal) {
Vector2 vp_he = camera_matrix.get_viewport_half_extents();
if (p_cam_vaspect) {
camera_matrix_square.set_orthogonal(vp_he.x * 2.0, 1.0, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], true);
} else {
camera_matrix_square.set_orthogonal(vp_he.y * 2.0, 1.0, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], false);
}
} else {
Vector2 vp_he = camera_matrix.get_viewport_half_extents();
if (p_cam_vaspect) {
camera_matrix_square.set_frustum(vp_he.x * 2.0, 1.0, Vector2(), distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], true);
} else {
camera_matrix_square.set_frustum(vp_he.y * 2.0, 1.0, Vector2(), distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], false);
}
}
Vector3 endpoints_square[8]; // frustum plane endpoints
res = camera_matrix_square.get_endpoints(p_cam_transform, endpoints_square);
ERR_CONTINUE(!res);
Vector3 center_square;
for (int j = 0; j < 8; j++) {
center_square += endpoints_square[j];
}
center_square /= 8.0;
real_t radius_square = 0;
for (int j = 0; j < 8; j++) {
real_t d = center_square.distance_to(endpoints_square[j]);
if (d > radius_square) {
radius_square = d;
}
}
radius_square *= texture_size / (texture_size - 2.0); //add a texel by each side
float z_max_square = z_vec.dot(center_square) + radius_square + pancake_size;
real_t z_min_cam_square = z_vec.dot(center_square) - radius_square;
aspect_bias_scale = (z_max - z_min_cam) / (z_max_square - z_min_cam_square);
// this is not entirely perfect, because the cull-adjusted z-max may be different
// but at least it's warranted that it results in a greater bias, so no acne should be present either way.
// pancaking also helps with this.
}
{ {
CameraMatrix ortho_camera; CameraMatrix ortho_camera;
@ -2188,7 +2121,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
cull.shadows[p_shadow_index].cascades[i].zfar = z_max - z_min_cam; cull.shadows[p_shadow_index].cascades[i].zfar = z_max - z_min_cam;
cull.shadows[p_shadow_index].cascades[i].split = distances[i + 1]; cull.shadows[p_shadow_index].cascades[i].split = distances[i + 1];
cull.shadows[p_shadow_index].cascades[i].shadow_texel_size = radius * 2.0 / texture_size; cull.shadows[p_shadow_index].cascades[i].shadow_texel_size = radius * 2.0 / texture_size;
cull.shadows[p_shadow_index].cascades[i].bias_scale = bias_scale * aspect_bias_scale * min_distance_bias_scale; cull.shadows[p_shadow_index].cascades[i].bias_scale = (z_max - z_min_cam);
cull.shadows[p_shadow_index].cascades[i].range_begin = z_max; cull.shadows[p_shadow_index].cascades[i].range_begin = z_max;
cull.shadows[p_shadow_index].cascades[i].uv_scale = uv_scale; cull.shadows[p_shadow_index].cascades[i].uv_scale = uv_scale;
} }