Fix directional shadow bias

* Simplified code a lot, bias based on normalized cascade size.
* Lets scale cascades, max distance, etc. without creating acne.
* Fixed normal biasing in directional shadows.

I removed normal biasing in both omni and spot shadows, since the technique can't be easily implemented there.
Will need to be replaced by something else.
This commit is contained in:
reduz 2021-07-29 13:30:39 -03:00
parent 8ee7adac1e
commit 55d357b1eb
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">
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 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="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.

View file

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

View file

@ -216,6 +216,10 @@ void Light3D::_validate_property(PropertyInfo &property) const {
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") {
property.usage = PROPERTY_USAGE_NONE;
}
@ -344,7 +348,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) {
set_param(PARAM_SHADOW_FADE_START, 0.8);
set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.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_TRANSMITTANCE_BIAS, 0.05);
set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1);
@ -426,7 +430,8 @@ DirectionalLight3D::DirectionalLight3D() :
set_param(PARAM_SHADOW_FADE_START, 0.8);
// Increase the default shadow bias to better suit most scenes.
// 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);
blend_splits = false;
}

View file

@ -2701,7 +2701,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
CameraMatrix shadow_mtx = rectm * bias * matrix * modelview;
light_data.shadow_split_offsets[j] = split;
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_transmittance_bias[j] = storage->light_get_transmittance_bias(base) * bias_scale;
light_data.shadow_z_range[j] = li->shadow_transform[j].farplane;
@ -2872,14 +2872,10 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const
light_data.shadow_enabled = true;
if (type == RS::LIGHT_SPOT) {
light_data.shadow_bias = (storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) * radius / 10.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;
light_data.shadow_bias = (storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) / 100.0);
} 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);
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

@ -412,14 +412,8 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
vec4 v = vec4(vertex, 1.0);
vec4 splane = (omni_lights.data[idx].shadow_matrix * v);
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_len = length(splane.xyz); //need to remember shadow len from here
float shadow;
@ -528,7 +522,8 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
splane.xy /= splane.z;
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.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);
@ -704,27 +699,17 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
//there is a shadowmap
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;
vec4 splane = (spot_lights.data[idx].shadow_matrix * v);
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) {
//soft shadow
//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;

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);
real_t first_radius = 0.0;
real_t min_distance_bias_scale = distances[1];
cull.shadow_count = p_shadow_index + 1;
cull.shadows[p_shadow_index].cascade_count = splits;
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_max_cam = 0.f;
real_t bias_scale = 1.0;
real_t aspect_bias_scale = 1.0;
//real_t bias_scale = 1.0;
//real_t aspect_bias_scale = 1.0;
//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
if (i == 0) {
first_radius = radius;
} else {
bias_scale = radius / first_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
if (pancake_size > 0) {
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.
}
z_max = z_vec.dot(center) + radius + pancake_size;
{
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].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].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].uv_scale = uv_scale;
}