From b0a6d30afc770f4502980299a5b7112225e0854f Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 21 Jul 2024 21:09:29 -0400 Subject: [PATCH] render: fix directional light shadows --- lyra-game/src/render/shaders/base.wgsl | 40 +++++++++++++++----------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/lyra-game/src/render/shaders/base.wgsl b/lyra-game/src/render/shaders/base.wgsl index 0ad3344..6103938 100755 --- a/lyra-game/src/render/shaders/base.wgsl +++ b/lyra-game/src/render/shaders/base.wgsl @@ -183,7 +183,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]]; let frag_pos_light_space = shadow_u.light_space_matrix * vec4(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); } 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); @@ -265,7 +265,10 @@ fn get_shadow_settings(shadow_u: LightShadowMapUniform) -> vec2 { } } -fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light_space: vec4, atlas_dimensions: vec2, shadow_u: LightShadowMapUniform) -> f32 { +fn calc_shadow_dir_light(world_pos: vec3, world_normal: vec3, light_dir: vec3, 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(world_pos, 1.0); + var proj_coords = frag_pos_light_space.xyz / frag_pos_light_space.w; // for some reason the y component is flipped after transforming proj_coords.y = -proj_coords.y; @@ -278,28 +281,28 @@ fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light let current_depth = proj_coords.z - bias; // get settings - let settings = get_shadow_settings(shadow_u); + let settings = get_shadow_settings(map_data); let pcf_samples_num = settings.x; let pcss_blocker_search_samples = settings.y; var shadow = 0.0; // hardware 2x2 PCF via camparison sampler 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); } // PCSS 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 else if pcf_samples_num > 0u { - let texel_size = 1.0 / f32(shadow_u.atlas_frame.width); - shadow = pcf_dir_light(xy_remapped, current_depth, shadow_u, texel_size); + let texel_size = 1.0 / f32(map_data.atlas_frame.width); + shadow = pcf_dir_light(xy_remapped, current_depth, map_data, texel_size); } // no filtering 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); 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. -fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2) -> vec2 { +/// +/// 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, safety_offset: bool) -> vec2 { let atlas_dimensions = textureDimensions(t_shadow_maps_atlas); // get the rect of the frame as a vec4 @@ -332,9 +338,9 @@ fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2) -> // put the frame rect in atlas UV space region_rect /= f32(atlas_dimensions.x); - // calculate a relatively tiny offset to avoid getting the end of 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; + // if safety_offset is true, calculate a relatively tiny offset to avoid getting the end of + // the frame and causing linear or nearest filtering to bleed to the adjacent frame. + let texel_size = select(0.0, (1.0 / f32(shadow_u.atlas_frame.x)) * 4.0, safety_offset); // lerp input coords let region_coords = vec2( @@ -354,7 +360,7 @@ fn find_blocker_distance_dir_light(tex_coords: vec2, receiver_depth: f32, b let samples = i32(u_shadow_settings.pcss_blocker_search_samples); for (var i = 0; i < samples; i++) { 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); if z < (receiver_depth - bias) { @@ -390,7 +396,7 @@ fn pcf_dir_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMa let samples_num = i32(u_shadow_settings.pcf_samples_num); for (var i = 0; i < samples_num; i++) { 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); } @@ -431,7 +437,7 @@ fn calc_shadow_point_light(world_pos: vec3, world_normal: vec3, light_ var shadow = 0.0; // hardware 2x2 PCF via camparison sampler 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); } // PCSS @@ -446,7 +452,7 @@ fn calc_shadow_point_light(world_pos: vec3, world_normal: vec3, light_ } // no filtering 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); shadow = select(1.0, 0.0, current_depth > closest_depth); } @@ -467,7 +473,7 @@ fn pcf_point_light(tex_coords: vec3, test_depth: f32, shadow_us: array