Better depth biases (#23)

* 3d_scene_pipelined: Use a shallower directional light angle to provoke acne

* cornell_box_pipelined: Remove bias tweaks

* bevy_pbr2: Simplify shadow biases by moving them to linear depth
This commit is contained in:
Robert Swain 2021-07-17 00:41:56 +02:00 committed by Carter Anderson
parent 30b8324672
commit 44df4c1fae
5 changed files with 54 additions and 53 deletions

View file

@ -199,7 +199,7 @@ fn setup(
}, },
transform: Transform { transform: Transform {
translation: Vec3::new(0.0, 2.0, 0.0), translation: Vec3::new(0.0, 2.0, 0.0),
rotation: Quat::from_rotation_x(-1.2), rotation: Quat::from_rotation_x(-std::f32::consts::FRAC_PI_4),
..Default::default() ..Default::default()
}, },
..Default::default() ..Default::default()

View file

@ -136,8 +136,6 @@ fn setup(
builder.spawn_bundle(PointLightBundle { builder.spawn_bundle(PointLightBundle {
point_light: PointLight { point_light: PointLight {
color: Color::WHITE, color: Color::WHITE,
shadow_bias_min: 0.00001,
shadow_bias_max: 0.0001,
intensity: 25.0, intensity: 25.0,
..Default::default() ..Default::default()
}, },
@ -150,8 +148,6 @@ fn setup(
commands.spawn_bundle(DirectionalLightBundle { commands.spawn_bundle(DirectionalLightBundle {
directional_light: DirectionalLight { directional_light: DirectionalLight {
illuminance: 10000.0, illuminance: 10000.0,
shadow_bias_min: 0.00001,
shadow_bias_max: 0.0001,
shadow_projection: OrthographicProjection { shadow_projection: OrthographicProjection {
left: -HALF_SIZE, left: -HALF_SIZE,
right: HALF_SIZE, right: HALF_SIZE,

View file

@ -7,8 +7,8 @@ pub struct PointLight {
pub intensity: f32, pub intensity: f32,
pub range: f32, pub range: f32,
pub radius: f32, pub radius: f32,
pub shadow_bias_min: f32, pub shadow_depth_bias: f32,
pub shadow_bias_max: f32, pub shadow_normal_bias: f32,
} }
impl Default for PointLight { impl Default for PointLight {
@ -18,15 +18,15 @@ impl Default for PointLight {
intensity: 200.0, intensity: 200.0,
range: 20.0, range: 20.0,
radius: 0.0, radius: 0.0,
shadow_bias_min: Self::DEFAULT_SHADOW_BIAS_MIN, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_bias_max: Self::DEFAULT_SHADOW_BIAS_MAX, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
} }
} }
} }
impl PointLight { impl PointLight {
pub const DEFAULT_SHADOW_BIAS_MIN: f32 = 0.00005; pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02;
pub const DEFAULT_SHADOW_BIAS_MAX: f32 = 0.002; pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.02;
} }
/// A Directional light. /// A Directional light.
@ -60,8 +60,8 @@ pub struct DirectionalLight {
pub color: Color, pub color: Color,
pub illuminance: f32, pub illuminance: f32,
pub shadow_projection: OrthographicProjection, pub shadow_projection: OrthographicProjection,
pub shadow_bias_min: f32, pub shadow_depth_bias: f32,
pub shadow_bias_max: f32, pub shadow_normal_bias: f32,
} }
impl Default for DirectionalLight { impl Default for DirectionalLight {
@ -79,15 +79,15 @@ impl Default for DirectionalLight {
far: size, far: size,
..Default::default() ..Default::default()
}, },
shadow_bias_min: Self::DEFAULT_SHADOW_BIAS_MIN, shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_bias_max: Self::DEFAULT_SHADOW_BIAS_MAX, shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
} }
} }
} }
impl DirectionalLight { impl DirectionalLight {
pub const DEFAULT_SHADOW_BIAS_MIN: f32 = 0.00005; pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02;
pub const DEFAULT_SHADOW_BIAS_MAX: f32 = 0.002; pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.02;
} }
// Ambient light color. // Ambient light color.

View file

@ -29,8 +29,8 @@ pub struct ExtractedPointLight {
range: f32, range: f32,
radius: f32, radius: f32,
transform: GlobalTransform, transform: GlobalTransform,
shadow_bias_min: f32, shadow_depth_bias: f32,
shadow_bias_max: f32, shadow_normal_bias: f32,
} }
pub struct ExtractedDirectionalLight { pub struct ExtractedDirectionalLight {
@ -38,8 +38,8 @@ pub struct ExtractedDirectionalLight {
illuminance: f32, illuminance: f32,
direction: Vec3, direction: Vec3,
projection: Mat4, projection: Mat4,
shadow_bias_min: f32, shadow_depth_bias: f32,
shadow_bias_max: f32, shadow_normal_bias: f32,
} }
#[repr(C)] #[repr(C)]
@ -52,8 +52,8 @@ pub struct GpuPointLight {
radius: f32, radius: f32,
near: f32, near: f32,
far: f32, far: f32,
shadow_bias_min: f32, shadow_depth_bias: f32,
shadow_bias_max: f32, shadow_normal_bias: f32,
} }
#[repr(C)] #[repr(C)]
@ -62,8 +62,8 @@ pub struct GpuDirectionalLight {
view_projection: Mat4, view_projection: Mat4,
color: Vec4, color: Vec4,
dir_to_light: Vec3, dir_to_light: Vec3,
shadow_bias_min: f32, shadow_depth_bias: f32,
shadow_bias_max: f32, shadow_normal_bias: f32,
} }
#[repr(C)] #[repr(C)]
@ -235,8 +235,8 @@ pub fn extract_lights(
range: point_light.range, range: point_light.range,
radius: point_light.radius, radius: point_light.radius,
transform: *transform, transform: *transform,
shadow_bias_min: point_light.shadow_bias_min, shadow_depth_bias: point_light.shadow_depth_bias,
shadow_bias_max: point_light.shadow_bias_max, shadow_normal_bias: point_light.shadow_normal_bias,
}); });
} }
for (entity, directional_light, transform) in directional_lights.iter() { for (entity, directional_light, transform) in directional_lights.iter() {
@ -247,8 +247,8 @@ pub fn extract_lights(
illuminance: directional_light.illuminance, illuminance: directional_light.illuminance,
direction: transform.forward(), direction: transform.forward(),
projection: directional_light.shadow_projection.get_projection_matrix(), projection: directional_light.shadow_projection.get_projection_matrix(),
shadow_bias_min: directional_light.shadow_bias_min, shadow_depth_bias: directional_light.shadow_depth_bias,
shadow_bias_max: directional_light.shadow_bias_max, shadow_normal_bias: directional_light.shadow_normal_bias,
}); });
} }
} }
@ -443,8 +443,8 @@ pub fn prepare_lights(
near: 0.1, near: 0.1,
far: light.range, far: light.range,
// proj: projection, // proj: projection,
shadow_bias_min: light.shadow_bias_min, shadow_depth_bias: light.shadow_depth_bias,
shadow_bias_max: light.shadow_bias_max, shadow_normal_bias: light.shadow_normal_bias,
}; };
} }
@ -482,8 +482,8 @@ pub fn prepare_lights(
dir_to_light, dir_to_light,
// NOTE: * view is correct, it should not be view.inverse() here // NOTE: * view is correct, it should not be view.inverse() here
view_projection: projection * view, view_projection: projection * view,
shadow_bias_min: light.shadow_bias_min, shadow_depth_bias: light.shadow_depth_bias,
shadow_bias_max: light.shadow_bias_max, shadow_normal_bias: light.shadow_normal_bias,
}; };
let depth_texture_view = let depth_texture_view =

View file

@ -95,16 +95,16 @@ struct PointLight {
radius: f32; radius: f32;
near: f32; near: f32;
far: f32; far: f32;
shadow_bias_min: f32; shadow_depth_bias: f32;
shadow_bias_max: f32; shadow_normal_bias: f32;
}; };
struct DirectionalLight { struct DirectionalLight {
view_projection: mat4x4<f32>; view_projection: mat4x4<f32>;
color: vec4<f32>; color: vec4<f32>;
direction_to_light: vec3<f32>; direction_to_light: vec3<f32>;
shadow_bias_min: f32; shadow_depth_bias: f32;
shadow_bias_max: f32; shadow_normal_bias: f32;
}; };
[[block]] [[block]]
@ -379,7 +379,7 @@ fn directional_light(light: DirectionalLight, roughness: f32, NdotV: f32, normal
return (specular_light + diffuse) * light.color.rgb * NoL; return (specular_light + diffuse) * light.color.rgb * NoL;
} }
fn fetch_point_shadow(light_id: i32, frag_position: vec4<f32>, shadow_bias: f32) -> f32 { fn fetch_point_shadow(light_id: i32, frag_position: vec4<f32>) -> f32 {
let light = lights.point_lights[light_id]; let light = lights.point_lights[light_id];
// because the shadow maps align with the axes and the frustum planes are at 45 degrees // because the shadow maps align with the axes and the frustum planes are at 45 degrees
@ -412,11 +412,12 @@ fn fetch_point_shadow(light_id: i32, frag_position: vec4<f32>, shadow_bias: f32)
// a quad (2x2 fragments) being processed not being sampled, and this messing with // a quad (2x2 fragments) being processed not being sampled, and this messing with
// mip-mapping functionality. The shadow maps have no mipmaps so Level just samples // mip-mapping functionality. The shadow maps have no mipmaps so Level just samples
// from LOD 0. // from LOD 0.
let bias = 0.0001; return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth);
return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth - shadow_bias);
} }
fn fetch_directional_shadow(light_id: i32, homogeneous_coords: vec4<f32>, shadow_bias: f32) -> f32 { fn fetch_directional_shadow(light_id: i32, frag_position: vec4<f32>) -> f32 {
let light = lights.directional_lights[light_id];
let homogeneous_coords = light.view_projection * frag_position;
if (homogeneous_coords.w <= 0.0) { if (homogeneous_coords.w <= 0.0) {
return 1.0; return 1.0;
} }
@ -428,7 +429,7 @@ fn fetch_directional_shadow(light_id: i32, homogeneous_coords: vec4<f32>, shadow
// do the lookup, using HW PCF and comparison // do the lookup, using HW PCF and comparison
// NOTE: Due to non-uniform control flow above, we must use the level variant of the texture // NOTE: Due to non-uniform control flow above, we must use the level variant of the texture
// sampler to avoid use of implicit derivatives causing possible undefined behavior. // sampler to avoid use of implicit derivatives causing possible undefined behavior.
return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), homogeneous_coords.z * proj_correction - shadow_bias); return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), homogeneous_coords.z * proj_correction);
} }
struct FragmentInput { struct FragmentInput {
@ -521,23 +522,27 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
let n_directional_lights = i32(lights.n_directional_lights); let n_directional_lights = i32(lights.n_directional_lights);
for (var i: i32 = 0; i < n_point_lights; i = i + 1) { for (var i: i32 = 0; i < n_point_lights; i = i + 1) {
let light = lights.point_lights[i]; let light = lights.point_lights[i];
let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, N, V, R, F0, diffuse_color);
let dir_to_light = normalize(light.position.xyz - in.world_position.xyz); let dir_to_light = normalize(light.position.xyz - in.world_position.xyz);
let shadow_bias = max( let depth_bias = light.shadow_depth_bias * dir_to_light.xyz;
light.shadow_bias_max * (1.0 - dot(in.world_normal, dir_to_light)), let NdotL = dot(dir_to_light.xyz, in.world_normal.xyz);
light.shadow_bias_min let normal_bias = light.shadow_normal_bias * (1.0 - NdotL) * in.world_normal.xyz;
); let biased_position = vec4<f32>(in.world_position.xyz + depth_bias + normal_bias, in.world_position.w);
let shadow = fetch_point_shadow(i, in.world_position, shadow_bias);
let shadow = fetch_point_shadow(i, biased_position);
let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, N, V, R, F0, diffuse_color);
light_accum = light_accum + light_contrib * shadow; light_accum = light_accum + light_contrib * shadow;
} }
for (var i: i32 = 0; i < n_directional_lights; i = i + 1) { for (var i: i32 = 0; i < n_directional_lights; i = i + 1) {
let light = lights.directional_lights[i]; let light = lights.directional_lights[i];
let depth_bias = light.shadow_depth_bias * light.direction_to_light.xyz;
let NdotL = dot(light.direction_to_light.xyz, in.world_normal.xyz);
let normal_bias = light.shadow_normal_bias * (1.0 - NdotL) * in.world_normal.xyz;
let biased_position = vec4<f32>(in.world_position.xyz + depth_bias + normal_bias, in.world_position.w);
let shadow = fetch_directional_shadow(i, biased_position);
let light_contrib = directional_light(light, roughness, NdotV, N, V, R, F0, diffuse_color); let light_contrib = directional_light(light, roughness, NdotV, N, V, R, F0, diffuse_color);
let shadow_bias = max(
light.shadow_bias_max * (1.0 - dot(in.world_normal, light.direction_to_light.xyz)),
light.shadow_bias_min
);
let shadow = fetch_directional_shadow(i, light.view_projection * in.world_position, shadow_bias);
light_accum = light_accum + light_contrib * shadow; light_accum = light_accum + light_contrib * shadow;
} }