render: fix directional light shadows

This commit is contained in:
SeanOMik 2024-07-21 21:09:29 -04:00
parent fef709d5f1
commit b0a6d30afc
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
1 changed files with 23 additions and 17 deletions

View File

@ -183,7 +183,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]]; let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]];
let frag_pos_light_space = shadow_u.light_space_matrix * vec4<f32>(in.world_position, 1.0); let frag_pos_light_space = shadow_u.light_space_matrix * vec4<f32>(in.world_position, 1.0);
let shadow = calc_shadow_dir_light(in.world_normal, light_dir, frag_pos_light_space, atlas_dimensions, shadow_u); let shadow = calc_shadow_dir_light(in.world_position, in.world_normal, light_dir, light);
light_res += blinn_phong_dir_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow); light_res += blinn_phong_dir_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow);
} else if (light.light_ty == LIGHT_TY_POINT) { } else if (light.light_ty == LIGHT_TY_POINT) {
let shadow = calc_shadow_point_light(in.world_position, in.world_normal, light_dir, light, atlas_dimensions); let shadow = calc_shadow_point_light(in.world_position, in.world_normal, light_dir, light, atlas_dimensions);
@ -265,7 +265,10 @@ fn get_shadow_settings(shadow_u: LightShadowMapUniform) -> vec2<u32> {
} }
} }
fn calc_shadow_dir_light(normal: vec3<f32>, light_dir: vec3<f32>, frag_pos_light_space: vec4<f32>, atlas_dimensions: vec2<i32>, shadow_u: LightShadowMapUniform) -> f32 { fn calc_shadow_dir_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, light: Light) -> f32 {
let map_data: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]];
let frag_pos_light_space = map_data.light_space_matrix * vec4<f32>(world_pos, 1.0);
var proj_coords = frag_pos_light_space.xyz / frag_pos_light_space.w; var proj_coords = frag_pos_light_space.xyz / frag_pos_light_space.w;
// for some reason the y component is flipped after transforming // for some reason the y component is flipped after transforming
proj_coords.y = -proj_coords.y; proj_coords.y = -proj_coords.y;
@ -278,28 +281,28 @@ fn calc_shadow_dir_light(normal: vec3<f32>, light_dir: vec3<f32>, frag_pos_light
let current_depth = proj_coords.z - bias; let current_depth = proj_coords.z - bias;
// get settings // get settings
let settings = get_shadow_settings(shadow_u); let settings = get_shadow_settings(map_data);
let pcf_samples_num = settings.x; let pcf_samples_num = settings.x;
let pcss_blocker_search_samples = settings.y; let pcss_blocker_search_samples = settings.y;
var shadow = 0.0; var shadow = 0.0;
// hardware 2x2 PCF via camparison sampler // hardware 2x2 PCF via camparison sampler
if pcf_samples_num == 2u { if pcf_samples_num == 2u {
let region_coords = to_atlas_frame_coords(shadow_u, xy_remapped); let region_coords = to_atlas_frame_coords(map_data, xy_remapped, false);
shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth); shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth);
} }
// PCSS // PCSS
else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u { else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u {
shadow = pcss_dir_light(xy_remapped, current_depth, shadow_u); shadow = pcss_dir_light(xy_remapped, current_depth, map_data);
} }
// only PCF // only PCF
else if pcf_samples_num > 0u { else if pcf_samples_num > 0u {
let texel_size = 1.0 / f32(shadow_u.atlas_frame.width); let texel_size = 1.0 / f32(map_data.atlas_frame.width);
shadow = pcf_dir_light(xy_remapped, current_depth, shadow_u, texel_size); shadow = pcf_dir_light(xy_remapped, current_depth, map_data, texel_size);
} }
// no filtering // no filtering
else { else {
let region_coords = to_atlas_frame_coords(shadow_u, xy_remapped); let region_coords = to_atlas_frame_coords(map_data, xy_remapped, false);
let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, region_coords, 0.0); let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, region_coords, 0.0);
shadow = select(1.0, 0.0, current_depth > closest_depth); shadow = select(1.0, 0.0, current_depth > closest_depth);
} }
@ -323,7 +326,10 @@ fn search_width(light_near: f32, uv_light_size: f32, receiver_depth: f32) -> f32
} }
/// Convert texture coords to be texture coords of an atlas frame. /// Convert texture coords to be texture coords of an atlas frame.
fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2<f32>) -> vec2<f32> { ///
/// If `safety_offset` is true, the frame will be shrank by a tiny amount to avoid bleeding
/// into adjacent frames from fiiltering.
fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2<f32>, safety_offset: bool) -> vec2<f32> {
let atlas_dimensions = textureDimensions(t_shadow_maps_atlas); let atlas_dimensions = textureDimensions(t_shadow_maps_atlas);
// get the rect of the frame as a vec4 // get the rect of the frame as a vec4
@ -332,9 +338,9 @@ fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2<f32>) ->
// put the frame rect in atlas UV space // put the frame rect in atlas UV space
region_rect /= f32(atlas_dimensions.x); region_rect /= f32(atlas_dimensions.x);
// calculate a relatively tiny offset to avoid getting the end of the frame and causing // if safety_offset is true, calculate a relatively tiny offset to avoid getting the end of
// linear or nearest filtering to bleed to the adjacent frame. // the frame and causing linear or nearest filtering to bleed to the adjacent frame.
let texel_size = (1.0 / f32(shadow_u.atlas_frame.x)) * 4.0; let texel_size = select(0.0, (1.0 / f32(shadow_u.atlas_frame.x)) * 4.0, safety_offset);
// lerp input coords // lerp input coords
let region_coords = vec2<f32>( let region_coords = vec2<f32>(
@ -354,7 +360,7 @@ fn find_blocker_distance_dir_light(tex_coords: vec2<f32>, receiver_depth: f32, b
let samples = i32(u_shadow_settings.pcss_blocker_search_samples); let samples = i32(u_shadow_settings.pcss_blocker_search_samples);
for (var i = 0; i < samples; i++) { for (var i = 0; i < samples; i++) {
let offset_coords = tex_coords + u_pcss_poisson_disc[i] * search_width; let offset_coords = tex_coords + u_pcss_poisson_disc[i] * search_width;
let new_coords = to_atlas_frame_coords(shadow_u, offset_coords); let new_coords = to_atlas_frame_coords(shadow_u, offset_coords, false);
let z = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, new_coords, 0.0); let z = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, new_coords, 0.0);
if z < (receiver_depth - bias) { if z < (receiver_depth - bias) {
@ -390,7 +396,7 @@ fn pcf_dir_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMa
let samples_num = i32(u_shadow_settings.pcf_samples_num); let samples_num = i32(u_shadow_settings.pcf_samples_num);
for (var i = 0; i < samples_num; i++) { for (var i = 0; i < samples_num; i++) {
let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius; let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius;
let new_coords = to_atlas_frame_coords(shadow_u, offset); let new_coords = to_atlas_frame_coords(shadow_u, offset, false);
shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth);
} }
@ -431,7 +437,7 @@ fn calc_shadow_point_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_
var shadow = 0.0; var shadow = 0.0;
// hardware 2x2 PCF via camparison sampler // hardware 2x2 PCF via camparison sampler
if pcf_samples_num == 2u { if pcf_samples_num == 2u {
let region_coords = to_atlas_frame_coords(u, coords_2d); let region_coords = to_atlas_frame_coords(u, coords_2d, true);
shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth); shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth);
} }
// PCSS // PCSS
@ -446,7 +452,7 @@ fn calc_shadow_point_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_
} }
// no filtering // no filtering
else { else {
let region_coords = to_atlas_frame_coords(u, coords_2d); let region_coords = to_atlas_frame_coords(u, coords_2d, true);
let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, region_coords, 0.0); let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, region_coords, 0.0);
shadow = select(1.0, 0.0, current_depth > closest_depth); shadow = select(1.0, 0.0, current_depth > closest_depth);
} }
@ -467,7 +473,7 @@ fn pcf_point_light(tex_coords: vec3<f32>, test_depth: f32, shadow_us: array<Ligh
coords_2d += u_pcf_poisson_disc[i] * uv_radius; coords_2d += u_pcf_poisson_disc[i] * uv_radius;
let new_coords = to_atlas_frame_coords(shadow_u, coords_2d); let new_coords = to_atlas_frame_coords(shadow_u, coords_2d, true);
shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth);
} }
shadow /= f32(samples_num); shadow /= f32(samples_num);