Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix directional shadow bias #51025

Merged
merged 1 commit into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion doc/classes/DirectionalLight3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions doc/classes/SpotLight3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
9 changes: 7 additions & 2 deletions scene/3d/light_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 3 additions & 7 deletions servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used 100 to avoid integer units for the normalized bias. Could be 1000 I don know.

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;
Expand Down Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down
75 changes: 4 additions & 71 deletions servers/rendering/renderer_scene_cull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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;

{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down