Skip to content

Commit

Permalink
Better depth biases (#23)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
superdump authored and cart committed Jul 24, 2021
1 parent 30b8324 commit 44df4c1
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 53 deletions.
2 changes: 1 addition & 1 deletion examples/3d/3d_scene_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ fn setup(
},
transform: Transform {
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()
Expand Down
4 changes: 0 additions & 4 deletions examples/3d/cornell_box_pipelined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ fn setup(
builder.spawn_bundle(PointLightBundle {
point_light: PointLight {
color: Color::WHITE,
shadow_bias_min: 0.00001,
shadow_bias_max: 0.0001,
intensity: 25.0,
..Default::default()
},
Expand All @@ -150,8 +148,6 @@ fn setup(
commands.spawn_bundle(DirectionalLightBundle {
directional_light: DirectionalLight {
illuminance: 10000.0,
shadow_bias_min: 0.00001,
shadow_bias_max: 0.0001,
shadow_projection: OrthographicProjection {
left: -HALF_SIZE,
right: HALF_SIZE,
Expand Down
24 changes: 12 additions & 12 deletions pipelined/bevy_pbr2/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pub struct PointLight {
pub intensity: f32,
pub range: f32,
pub radius: f32,
pub shadow_bias_min: f32,
pub shadow_bias_max: f32,
pub shadow_depth_bias: f32,
pub shadow_normal_bias: f32,
}

impl Default for PointLight {
Expand All @@ -18,15 +18,15 @@ impl Default for PointLight {
intensity: 200.0,
range: 20.0,
radius: 0.0,
shadow_bias_min: Self::DEFAULT_SHADOW_BIAS_MIN,
shadow_bias_max: Self::DEFAULT_SHADOW_BIAS_MAX,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
}
}
}

impl PointLight {
pub const DEFAULT_SHADOW_BIAS_MIN: f32 = 0.00005;
pub const DEFAULT_SHADOW_BIAS_MAX: f32 = 0.002;
pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02;
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.02;
}

/// A Directional light.
Expand Down Expand Up @@ -60,8 +60,8 @@ pub struct DirectionalLight {
pub color: Color,
pub illuminance: f32,
pub shadow_projection: OrthographicProjection,
pub shadow_bias_min: f32,
pub shadow_bias_max: f32,
pub shadow_depth_bias: f32,
pub shadow_normal_bias: f32,
}

impl Default for DirectionalLight {
Expand All @@ -79,15 +79,15 @@ impl Default for DirectionalLight {
far: size,
..Default::default()
},
shadow_bias_min: Self::DEFAULT_SHADOW_BIAS_MIN,
shadow_bias_max: Self::DEFAULT_SHADOW_BIAS_MAX,
shadow_depth_bias: Self::DEFAULT_SHADOW_DEPTH_BIAS,
shadow_normal_bias: Self::DEFAULT_SHADOW_NORMAL_BIAS,
}
}
}

impl DirectionalLight {
pub const DEFAULT_SHADOW_BIAS_MIN: f32 = 0.00005;
pub const DEFAULT_SHADOW_BIAS_MAX: f32 = 0.002;
pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02;
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.02;
}

// Ambient light color.
Expand Down
32 changes: 16 additions & 16 deletions pipelined/bevy_pbr2/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ pub struct ExtractedPointLight {
range: f32,
radius: f32,
transform: GlobalTransform,
shadow_bias_min: f32,
shadow_bias_max: f32,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
}

pub struct ExtractedDirectionalLight {
color: Color,
illuminance: f32,
direction: Vec3,
projection: Mat4,
shadow_bias_min: f32,
shadow_bias_max: f32,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
}

#[repr(C)]
Expand All @@ -52,8 +52,8 @@ pub struct GpuPointLight {
radius: f32,
near: f32,
far: f32,
shadow_bias_min: f32,
shadow_bias_max: f32,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
}

#[repr(C)]
Expand All @@ -62,8 +62,8 @@ pub struct GpuDirectionalLight {
view_projection: Mat4,
color: Vec4,
dir_to_light: Vec3,
shadow_bias_min: f32,
shadow_bias_max: f32,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
}

#[repr(C)]
Expand Down Expand Up @@ -235,8 +235,8 @@ pub fn extract_lights(
range: point_light.range,
radius: point_light.radius,
transform: *transform,
shadow_bias_min: point_light.shadow_bias_min,
shadow_bias_max: point_light.shadow_bias_max,
shadow_depth_bias: point_light.shadow_depth_bias,
shadow_normal_bias: point_light.shadow_normal_bias,
});
}
for (entity, directional_light, transform) in directional_lights.iter() {
Expand All @@ -247,8 +247,8 @@ pub fn extract_lights(
illuminance: directional_light.illuminance,
direction: transform.forward(),
projection: directional_light.shadow_projection.get_projection_matrix(),
shadow_bias_min: directional_light.shadow_bias_min,
shadow_bias_max: directional_light.shadow_bias_max,
shadow_depth_bias: directional_light.shadow_depth_bias,
shadow_normal_bias: directional_light.shadow_normal_bias,
});
}
}
Expand Down Expand Up @@ -443,8 +443,8 @@ pub fn prepare_lights(
near: 0.1,
far: light.range,
// proj: projection,
shadow_bias_min: light.shadow_bias_min,
shadow_bias_max: light.shadow_bias_max,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
};
}

Expand Down Expand Up @@ -482,8 +482,8 @@ pub fn prepare_lights(
dir_to_light,
// NOTE: * view is correct, it should not be view.inverse() here
view_projection: projection * view,
shadow_bias_min: light.shadow_bias_min,
shadow_bias_max: light.shadow_bias_max,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
};

let depth_texture_view =
Expand Down
45 changes: 25 additions & 20 deletions pipelined/bevy_pbr2/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@ struct PointLight {
radius: f32;
near: f32;
far: f32;
shadow_bias_min: f32;
shadow_bias_max: f32;
shadow_depth_bias: f32;
shadow_normal_bias: f32;
};

struct DirectionalLight {
view_projection: mat4x4<f32>;
color: vec4<f32>;
direction_to_light: vec3<f32>;
shadow_bias_min: f32;
shadow_bias_max: f32;
shadow_depth_bias: f32;
shadow_normal_bias: f32;
};

[[block]]
Expand Down Expand Up @@ -379,7 +379,7 @@ fn directional_light(light: DirectionalLight, roughness: f32, NdotV: f32, normal
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];

// because the shadow maps align with the axes and the frustum planes are at 45 degrees
Expand Down Expand Up @@ -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
// mip-mapping functionality. The shadow maps have no mipmaps so Level just samples
// from LOD 0.
let bias = 0.0001;
return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth - shadow_bias);
return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth);
}

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) {
return 1.0;
}
Expand All @@ -428,7 +429,7 @@ fn fetch_directional_shadow(light_id: i32, homogeneous_coords: vec4<f32>, shadow
// 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
// 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 {
Expand Down Expand Up @@ -521,23 +522,27 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
let n_directional_lights = i32(lights.n_directional_lights);
for (var i: i32 = 0; i < n_point_lights; i = i + 1) {
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 shadow_bias = max(
light.shadow_bias_max * (1.0 - dot(in.world_normal, dir_to_light)),
light.shadow_bias_min
);
let shadow = fetch_point_shadow(i, in.world_position, shadow_bias);
let depth_bias = light.shadow_depth_bias * dir_to_light.xyz;
let NdotL = dot(dir_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_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;
}
for (var i: i32 = 0; i < n_directional_lights; i = i + 1) {
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 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;
}

Expand Down

0 comments on commit 44df4c1

Please sign in to comment.