Skip to content

receiver plane bias #40

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

Open
wants to merge 1 commit into
base: receiver-plane-depth-bias
Choose a base branch
from
Open
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
11 changes: 5 additions & 6 deletions crates/bevy_pbr/src/render/shadow_sampling.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,12 @@ fn compute_receiver_plane_depth_bias(tex_coord_dx: vec3<f32>, tex_coord_dy: vec3
return bias_uv;
}

fn sample_shadow_map(light_local: vec2<f32>, depth: f32, array_index: i32, texel_size: f32) -> f32 {
fn sample_shadow_map(light_local_plus_depth: vec3<f32>, array_index: i32, texel_size: f32, dx: vec3<f32>, dy: vec3<f32>) -> f32 {
let light_local = light_local_plus_depth.xy;
let depth = light_local_plus_depth.z;

#ifndef SHADOW_FILTER_METHOD_HARDWARE_2X2
let shadow_pos = vec3(light_local, depth);
let receiver_plane_depth_bias = compute_receiver_plane_depth_bias(
dpdx(shadow_pos),
dpdy(shadow_pos),
);
let receiver_plane_depth_bias = compute_receiver_plane_depth_bias(dx, dy);
#endif

#ifdef SHADOW_FILTER_METHOD_CASTANO_13
Expand Down
61 changes: 47 additions & 14 deletions crates/bevy_pbr/src/render/shadows.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ fn fetch_spot_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: ve
// 0.1 must match POINT_LIGHT_NEAR_Z
let depth = 0.1 / -projected_position.z;

// Number determined by trial and error that gave nice results.
let texel_size = 0.0134277345;
return sample_shadow_map(shadow_uv, depth, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, texel_size);
// Number determined by trial and error that gave nice results.
let texel_size = 0.0134277345;

let light_local = vec3(shadow_uv, depth);
return sample_shadow_map(light_local, i32(light_id) + view_bindings::lights.spot_light_shadowmap_offset, texel_size, dpdx(light_local), dpdy(light_local));
}

fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
Expand All @@ -113,7 +115,9 @@ fn get_cascade_index(light_id: u32, view_z: f32) -> u32 {
return (*light).num_cascades;
}

fn sample_directional_cascade(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
// transform world position to cascade light-space position (xyz) + valid flag (w)
fn get_directional_light_local(light_id: u32, cascade_index: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> vec4<f32> {
var valid = 1.0;
let light = &view_bindings::lights.directional_lights[light_id];
let cascade = &(*light).cascades[cascade_index];

Expand All @@ -124,44 +128,73 @@ fn sample_directional_cascade(light_id: u32, cascade_index: u32, frag_position:

let offset_position_clip = (*cascade).view_projection * offset_position;
if (offset_position_clip.w <= 0.0) {
return 1.0;
valid = 0.0;
}
let offset_position_ndc = offset_position_clip.xyz / offset_position_clip.w;
// No shadow outside the orthographic projection volume
if (any(offset_position_ndc.xy < vec2<f32>(-1.0)) || offset_position_ndc.z < 0.0
|| any(offset_position_ndc > vec3<f32>(1.0))) {
return 1.0;
valid = 0.0;
}

// compute texture coordinates for shadow lookup, compensating for the Y-flip difference
// between the NDC and texture coordinates
let flip_correction = vec2<f32>(0.5, -0.5);
let light_local = offset_position_ndc.xy * flip_correction + vec2<f32>(0.5, 0.5);

let depth = offset_position_ndc.z;
let light_local = offset_position_ndc.xyz * vec3<f32>(0.5, -0.5, 1.0) + vec3<f32>(0.5, 0.5, 0.0);
return vec4<f32>(light_local, valid);
}

fn sample_directional_cascade(light_id: u32, cascade_index: u32, light_local: vec3<f32>, dx: vec3<f32>, dy: vec3<f32>) -> f32 {
let light = &view_bindings::lights.directional_lights[light_id];
let cascade = &(*light).cascades[cascade_index];
let array_index = i32((*light).depth_texture_base_index + cascade_index);
return sample_shadow_map(light_local, depth, array_index, (*cascade).texel_size);
return sample_shadow_map(light_local, array_index, (*cascade).texel_size, dx, dy);
}

fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>, view_z: f32) -> f32 {
let light = &view_bindings::lights.directional_lights[light_id];
let cascade_index = get_cascade_index(light_id, view_z);

// we need derivatives of the light-local position, but due to non-uniform cascade accesses those are not in uniform control flow directly
// and dpdx / dpdy can't be used. so we take derivatives of the frag position instead, and manually calculate light-local derivatives
let dpdx_frag_position = dpdx(frag_position);
let dpdy_frag_position = dpdy(frag_position);

if (cascade_index >= (*light).num_cascades) {
return 1.0;
}

var shadow = sample_directional_cascade(light_id, cascade_index, frag_position, surface_normal);
let light_local_valid = get_directional_light_local(light_id, cascade_index, frag_position, surface_normal);
let light_local = light_local_valid.xyz;
let valid = light_local_valid.w;

let light_local_dx = get_directional_light_local(light_id, cascade_index, frag_position + dpdx_frag_position, surface_normal).xyz - light_local;
let light_local_dy = get_directional_light_local(light_id, cascade_index, frag_position + dpdy_frag_position, surface_normal).xyz - light_local;

var shadow = 1.0;
if valid != 0.0 {
shadow = sample_directional_cascade(light_id, cascade_index, light_local, light_local_dx, light_local_dy);
} else {
// outside frustum
}

// Blend with the next cascade, if there is one.
let next_cascade_index = cascade_index + 1u;
if (next_cascade_index < (*light).num_cascades) {
let this_far_bound = (*light).cascades[cascade_index].far_bound;
let next_near_bound = (1.0 - (*light).cascades_overlap_proportion) * this_far_bound;
if (-view_z >= next_near_bound) {
let next_shadow = sample_directional_cascade(light_id, next_cascade_index, frag_position, surface_normal);
shadow = mix(shadow, next_shadow, (-view_z - next_near_bound) / (this_far_bound - next_near_bound));
let next_light_local_valid = get_directional_light_local(light_id, next_cascade_index, frag_position, surface_normal);
let next_light_local = next_light_local_valid.xyz;
let next_valid = next_light_local_valid.w;
if next_valid != 0.0 {
// scale derivatives by texel ratio
let ratio = (*light).cascades[cascade_index].texel_size / (*light).cascades[next_cascade_index].texel_size;
let next_light_local_dx = light_local_dx * ratio;
let next_light_local_dy = light_local_dy * ratio;
// sample next cascade
let next_shadow = sample_directional_cascade(light_id, next_cascade_index, next_light_local, next_light_local_dx, next_light_local_dy);
shadow = mix(shadow, next_shadow, (-view_z - next_near_bound) / (this_far_bound - next_near_bound));
}
}
}
return shadow;
Expand Down