From a85178eeea1cd91782f1abfee5cffecb00d2ff5a Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 9 Aug 2024 21:51:56 -0400 Subject: [PATCH] Revert "render: shadow maps and PCF for spot lights" This reverts commit 8c1738334c2ee46317fa279ea9dd8acb61a8c366. --- examples/shadows/src/main.rs | 42 ++---- lyra-game/src/render/graph/passes/shadows.rs | 137 ++++++------------- lyra-game/src/render/light/spotlight.rs | 2 - lyra-game/src/render/shaders/base.wgsl | 95 ++++--------- lyra-game/src/render/shaders/shadows.wgsl | 5 + lyra-math/src/angle.rs | 16 +-- 6 files changed, 79 insertions(+), 218 deletions(-) diff --git a/examples/shadows/src/main.rs b/examples/shadows/src/main.rs index 30ad34a..ba929aa 100644 --- a/examples/shadows/src/main.rs +++ b/examples/shadows/src/main.rs @@ -5,10 +5,10 @@ use lyra_engine::{ Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput, }, - math::{self, Angle, Quat, Transform, Vec3}, + math::{self, Quat, Transform, Vec3}, render::{ graph::{ShadowCasterSettings, ShadowFilteringMode}, - light::{directional::DirectionalLight, PointLight, SpotLight}, + light::{directional::DirectionalLight, PointLight}, }, scene::{ CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, @@ -189,7 +189,7 @@ fn setup_scene_plugin(game: &mut Game) { light_tran, )); - /* world.spawn(( + world.spawn(( cube_mesh.clone(), PointLight { enabled: true, @@ -207,33 +207,6 @@ fn setup_scene_plugin(game: &mut Game) { Quat::IDENTITY, Vec3::new(0.5, 0.5, 0.5), ), - )); */ - - let t = Transform::new( - Vec3::new(4.0 - 1.43, -13.0, 0.0), - //Vec3::new(-5.0, 1.0, -0.28), - //Vec3::new(-10.0, 0.94, -0.28), - - Quat::from_euler(math::EulerRot::XYZ, 0.0, math::Angle::Degrees(-45.0).to_radians(), 0.0), - Vec3::new(0.15, 0.15, 0.15), - ); - - world.spawn(( - SpotLight { - enabled: true, - color: Vec3::new(1.0, 0.0, 0.0), - intensity: 3.0, - range: 4.5, - //cutoff: math::Angle::Degrees(45.0), - ..Default::default() - }, - /* ShadowCasterSettings { - filtering_mode: ShadowFilteringMode::Pcf, - ..Default::default() - }, */ - WorldTransform::from(t), - t, - //cube_mesh.clone(), )); /* world.spawn(( @@ -251,13 +224,14 @@ fn setup_scene_plugin(game: &mut Game) { let mut camera = CameraComponent::new_3d(); //camera.transform.translation += math::Vec3::new(0.0, 2.0, 10.5); - camera.transform.translation = math::Vec3::new(-1.0, -10.0, -1.5); + /* camera.transform.translation = math::Vec3::new(-3.0, -8.0, -3.0); camera.transform.rotate_x(math::Angle::Degrees(-27.0)); - camera.transform.rotate_y(math::Angle::Degrees(-90.0)); + camera.transform.rotate_y(math::Angle::Degrees(-55.0)); */ - /* camera.transform.translation = math::Vec3::new(15.0, -8.0, 1.0); + camera.transform.translation = math::Vec3::new(15.0, -8.0, 1.0); camera.transform.rotate_x(math::Angle::Degrees(-27.0)); - camera.transform.rotate_y(math::Angle::Degrees(90.0)); */ + //camera.transform.rotate_y(math::Angle::Degrees(-90.0)); + camera.transform.rotate_y(math::Angle::Degrees(90.0)); world.spawn((camera, FreeFlyCamera::default())); } diff --git a/lyra-game/src/render/graph/passes/shadows.rs b/lyra-game/src/render/graph/passes/shadows.rs index 5e59f59..3e207a9 100644 --- a/lyra-game/src/render/graph/passes/shadows.rs +++ b/lyra-game/src/render/graph/passes/shadows.rs @@ -20,7 +20,7 @@ use wgpu::util::DeviceExt; use crate::render::{ graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue}, - light::{directional::DirectionalLight, LightType, PointLight, SpotLight}, + light::{directional::DirectionalLight, LightType, PointLight}, resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, transform_buffer_storage::TransformBuffers, vertex::Vertex, @@ -186,7 +186,6 @@ impl ShadowMapsPass { light_type: LightType, entity: Entity, light_pos: Transform, - light_half_outer_angle: Option, are_settings_custom: bool, shadow_settings: ShadowCasterSettings, ) -> LightDepthMap { @@ -201,6 +200,17 @@ impl ShadowMapsPass { let has_shadow_settings = if are_settings_custom { 1 } else { 0 }; + /* let (has_shadow_settings, pcf_samples_num, pcss_samples_num) = if are_settings_custom { + + (1, u.pcf_samples_num, u.pcss_blocker_search_samples) + } else { + (0, , 0) + }; */ + + /* shadow_settings.map(|ss| { + let u = ShadowSettingsUniform::new(ss.filtering_mode, ss.pcf_samples_num, ss.pcss_blocker_search_samples); + (1, u.pcf_samples_num, u.pcss_blocker_search_samples) + }).unwrap_or((0, 0, 0)); */ let (start_atlas_idx, uniform_indices) = match light_type { LightType::Directional => { @@ -255,49 +265,7 @@ impl ShadowMapsPass { indices[0] = uniform_index; (atlas_index, indices) } - LightType::Spotlight => { - // allocate a single frame in the shadow map atlas - let atlas_index = atlas - .pack(SHADOW_SIZE.x as _, SHADOW_SIZE.y as _) - .expect("failed to pack new shadow map into texture atlas"); - let atlas_frame = atlas.texture_frame(atlas_index).expect("Frame missing"); - - let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32; - let projection = glam::Mat4::perspective_rh( - //Angle::Degrees(90.0).to_radians(), - (light_half_outer_angle.unwrap() * 2.0).to_radians(), - aspect, - shadow_settings.near_plane, - shadow_settings.far_plane, - ); - - let light_trans = light_pos.translation; - let forward = light_pos.forward(); - let up = light_pos.up(); - let view = glam::Mat4::look_to_rh(light_trans, forward, up); - - let light_proj = projection * view; - - let u = LightShadowUniform { - space_mat: light_proj, - atlas_frame, - near_plane: shadow_settings.near_plane, - far_plane: shadow_settings.far_plane, - light_size_uv: 0.0, - _padding1: 0, - light_pos: light_pos.translation, - has_shadow_settings, - pcf_samples_num: u.pcf_samples_num, - pcss_blocker_search_samples: u.pcss_blocker_search_samples, - constant_depth_bias: DEFAULT_CONSTANT_DEPTH_BIAS * shadow_settings.constant_depth_bias_scale, - _padding2: 0, - }; - - let uniform_index = self.light_uniforms_buffer.insert(queue, &u); - let mut indices = [0; 6]; - indices[0] = uniform_index; - (atlas_index, indices) - }, + LightType::Spotlight => todo!(), LightType::Point => { let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32; let projection = glam::Mat4::perspective_rh( @@ -658,31 +626,6 @@ impl Node for ShadowMapsPass { LightType::Directional, entity, *pos, - None, - custom_settings, - shadow_settings, - ); - index_components_queue.push_back((entity, atlas_index)); - } - } - - for (entity, pos, shadow_settings, spot) in world.view_iter::<( - Entities, - &Transform, - Option<&ShadowCasterSettings>, - &SpotLight, - )>() { - if !self.depth_maps.contains_key(&entity) { - let (custom_settings, shadow_settings) = shadow_settings - .map(|ss| (true, ss.clone())) - .unwrap_or((false, settings)); - - let atlas_index = self.create_depth_map( - &context.queue, - LightType::Spotlight, - entity, - *pos, - Some(spot.outer_cutoff), custom_settings, shadow_settings, ); @@ -706,7 +649,6 @@ impl Node for ShadowMapsPass { LightType::Point, entity, *pos, - None, custom_settings, shadow_settings, ); @@ -844,7 +786,7 @@ impl Node for ShadowMapsPass { &frame, light_depth_map.uniform_index[0] as _, ); - }, + } LightType::Point => { pass.set_pipeline(&point_light_pipeline); @@ -864,25 +806,8 @@ impl Node for ShadowMapsPass { ui as _, ); } - }, - LightType::Spotlight => { - pass.set_pipeline(&pipeline); - //pass.set_pipeline(&point_light_pipeline); - - let frame = atlas - .texture_frame(light_depth_map.atlas_index) - .expect("missing atlas frame for light"); - - light_shadow_pass_impl( - &mut pass, - &self.uniforms_bg, - &render_meshes, - &mesh_buffers, - &transforms, - &frame, - light_depth_map.uniform_index[0] as _, - ); } + LightType::Spotlight => todo!(), } } } @@ -1051,17 +976,35 @@ pub enum ShadowFilteringMode { None, /// Uses hardware features for 2x2 PCF. Pcf2x2, - #[default] Pcf, - /// Percentage-Closer Soft Shadows - /// https://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf - /// - /// PCSS is only implemented for directional lights. Use PCF for point and spot lights instead. - /// PCSS is expensive per-frame, so it has not been implemented for them. If you use this for - /// point and/or spot lights, the renderer will fall back to PCF. + #[default] Pcss, } +/* #[derive(Debug, Copy, Clone)] +pub struct ShadowSettings { + pub filtering_mode: ShadowFilteringMode, + /// How many PCF filtering samples are used per dimension. + /// + /// A value of 25 is common, this is maxed to 128. + pub pcf_samples_num: u32, + /// How many samples are used for the PCSS blocker search step. + /// + /// Multiple samples are required to avoid holes int he penumbra due to missing blockers. + /// A value of 25 is common, this is maxed to 128. + pub pcss_blocker_search_samples: u32, +} + +impl Default for ShadowSettings { + fn default() -> Self { + Self { + filtering_mode: ShadowFilteringMode::default(), + pcf_samples_num: 25, + pcss_blocker_search_samples: 25, + } + } +} */ + const PCF_SAMPLES_NUM_MAX: u32 = 128; const PCSS_SAMPLES_NUM_MAX: u32 = 128; diff --git a/lyra-game/src/render/light/spotlight.rs b/lyra-game/src/render/light/spotlight.rs index 4d5d8bf..fa89c5a 100644 --- a/lyra-game/src/render/light/spotlight.rs +++ b/lyra-game/src/render/light/spotlight.rs @@ -9,8 +9,6 @@ pub struct SpotLight { pub range: f32, pub intensity: f32, pub smoothness: f32, - /// Cutoff angle that specifies the light radius. - /// This is half of the light's FOV. pub cutoff: math::Angle, pub outer_cutoff: math::Angle, } diff --git a/lyra-game/src/render/shaders/base.wgsl b/lyra-game/src/render/shaders/base.wgsl index 3d6ecc0..2bab59e 100755 --- a/lyra-game/src/render/shaders/base.wgsl +++ b/lyra-game/src/render/shaders/base.wgsl @@ -182,6 +182,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { if (light.light_ty == LIGHT_TY_DIRECTIONAL) { 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_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); @@ -189,8 +190,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let shadow = calc_shadow_point_light(in.world_position, in.world_normal, light_dir, light, atlas_dimensions); light_res += blinn_phong_point_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow); } else if (light.light_ty == LIGHT_TY_SPOT) { - let shadow = calc_shadow_spot_light(in.world_position, in.world_normal, light_dir, light); - light_res += blinn_phong_spot_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow); + light_res += blinn_phong_spot_light(in.world_position, in.world_normal, light, u_material, specular_color); } } @@ -293,12 +293,12 @@ fn calc_shadow_dir_light(world_pos: vec3, world_normal: vec3, light_di } // PCSS else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u { - shadow = pcss_dir_light(xy_remapped, current_depth, i32(pcss_blocker_search_samples), i32(pcf_samples_num), map_data); + shadow = pcss_dir_light(xy_remapped, current_depth, map_data); } // only PCF else if pcf_samples_num > 0u { let texel_size = 1.0 / f32(map_data.atlas_frame.width); - shadow = pcf_dir_light(xy_remapped, current_depth, map_data, i32(pcf_samples_num), texel_size); + shadow = pcf_dir_light(xy_remapped, current_depth, map_data, texel_size); } // no filtering else { @@ -352,13 +352,13 @@ fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2, saf } /// Find the average blocker distance for a directiona llight -fn find_blocker_distance_dir_light(tex_coords: vec2, search_samples: i32, receiver_depth: f32, bias: f32, shadow_u: LightShadowMapUniform) -> vec2 { +fn find_blocker_distance_dir_light(tex_coords: vec2, receiver_depth: f32, bias: f32, shadow_u: LightShadowMapUniform) -> vec2 { let search_width = search_width(shadow_u.near_plane, shadow_u.light_size_uv, receiver_depth); var blockers = 0; var avg_dist = 0.0; - //let samples = i32(u_shadow_settings.pcss_blocker_search_samples); - for (var i = 0; i < search_samples; i++) { + 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, false); let z = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, new_coords, 0.0); @@ -373,8 +373,8 @@ fn find_blocker_distance_dir_light(tex_coords: vec2, search_samples: i32, r return vec2(avg_dist / b, b); } -fn pcss_dir_light(tex_coords: vec2, receiver_depth: f32, pcss_blocker_samples: i32, pcf_samples_num: i32, shadow_u: LightShadowMapUniform) -> f32 { - let blocker_search = find_blocker_distance_dir_light(tex_coords, pcss_blocker_samples, receiver_depth, 0.0, shadow_u); +fn pcss_dir_light(tex_coords: vec2, receiver_depth: f32, shadow_u: LightShadowMapUniform) -> f32 { + let blocker_search = find_blocker_distance_dir_light(tex_coords, receiver_depth, 0.0, shadow_u); // If no blockers were found, exit now to save in filtering if blocker_search.y == 0.0 { @@ -387,12 +387,13 @@ fn pcss_dir_light(tex_coords: vec2, receiver_depth: f32, pcss_blocker_sampl // PCF let uv_radius = penumbra_width * shadow_u.light_size_uv * shadow_u.near_plane / receiver_depth; - return pcf_dir_light(tex_coords, receiver_depth, shadow_u, pcf_samples_num, uv_radius); + return pcf_dir_light(tex_coords, receiver_depth, shadow_u, uv_radius); } /// Calculate the shadow coefficient using PCF of a directional light -fn pcf_dir_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: i32, uv_radius: f32) -> f32 { +fn pcf_dir_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMapUniform, uv_radius: f32) -> f32 { var shadow = 0.0; + 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, false); @@ -439,10 +440,15 @@ fn calc_shadow_point_light(world_pos: vec3, world_normal: vec3, light_ 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); } - // only PCF, PCSS is not supported so no need to check for it + // PCSS + else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u { + shadow = pcss_dir_light(coords_2d, current_depth, u); + } + // only PCF else if pcf_samples_num > 0u { let texel_size = 1.0 / f32(u.atlas_frame.width); - shadow = pcf_point_light(frag_to_light, current_depth, uniforms, pcf_samples_num, texel_size); + shadow = pcf_point_light(frag_to_light, current_depth, uniforms, pcf_samples_num, 0.007); + //shadow = pcf_point_light(coords_2d, current_depth, u, pcf_samples_num, texel_size); } // no filtering else { @@ -476,11 +482,11 @@ fn pcf_point_light(tex_coords: vec3, test_depth: f32, shadow_us: array, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: i32, uv_radius: f32) -> f32 { +/*fn pcf_point_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: u32, uv_radius: f32) -> f32 { var shadow = 0.0; - for (var i = 0; i < samples_num; i++) { + for (var i = 0; i < i32(samples_num); i++) { let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius; - let new_coords = to_atlas_frame_coords(shadow_u, offset, false); + let new_coords = to_atlas_frame_coords(shadow_u, offset); shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); } @@ -488,57 +494,7 @@ fn pcf_spot_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowM // clamp shadow to [0; 1] return saturate(shadow); -} - -fn calc_shadow_spot_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; - - // Remap xy to [0.0, 1.0] - let xy_remapped = proj_coords.xy * 0.5 + 0.5; - - // use a bias to avoid shadow acne - let current_depth = proj_coords.z - map_data.constant_depth_bias; - - // get settings - 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(map_data, xy_remapped, false); - shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth); - } - // only PCF is supported for spot lights - else if pcf_samples_num > 0u { - let texel_size = 1.0 / f32(map_data.atlas_frame.width); - shadow = pcf_spot_light(xy_remapped, current_depth, map_data, i32(pcf_samples_num), texel_size); - } - // no filtering - else { - 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); - } - - // dont cast shadows outside the light's far plane - if (proj_coords.z > 1.0) { - shadow = 1.0; - } - - // dont cast shadows if the texture coords would go past the shadow maps - if (xy_remapped.x > 1.0 || xy_remapped.x < 0.0 || xy_remapped.y > 1.0 || xy_remapped.y < 0.0) { - shadow = 1.0; - } - - return shadow; -} +}*/ fn debug_grid(in: VertexOutput) -> vec4 { let tile_index_float: vec2 = in.clip_position.xy / 16.0; @@ -618,7 +574,7 @@ fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_li return (shadow * (ambient_color + diffuse_color + specular_color)) * point_light.intensity; } -fn blinn_phong_spot_light(world_pos: vec3, world_norm: vec3, spot_light: Light, material: Material, specular_factor: vec3, shadow: f32) -> vec3 { +fn blinn_phong_spot_light(world_pos: vec3, world_norm: vec3, spot_light: Light, material: Material, specular_factor: vec3) -> vec3 { let light_color = spot_light.color; let light_pos = spot_light.position; let camera_view_pos = u_camera.position; @@ -659,8 +615,7 @@ fn blinn_phong_spot_light(world_pos: vec3, world_norm: vec3, spot_ligh //// end of spot light attenuation //// - //return /*ambient_color +*/ diffuse_color + specular_color; - return (shadow * (diffuse_color + specular_color)); + return /*ambient_color +*/ diffuse_color + specular_color; } fn calc_attenuation(light: Light, distance: f32) -> f32 { diff --git a/lyra-game/src/render/shaders/shadows.wgsl b/lyra-game/src/render/shaders/shadows.wgsl index b924227..58be2c5 100644 --- a/lyra-game/src/render/shaders/shadows.wgsl +++ b/lyra-game/src/render/shaders/shadows.wgsl @@ -24,10 +24,15 @@ struct LightShadowMapUniform { @group(0) @binding(0) var u_light_shadow: array; +/*@group(0) @binding(1) +var u_light_pos: vec3; +@group(0) @binding(2) +var u_light_far_plane: f32;*/ @group(1) @binding(0) var u_model_transform_data: TransformData; + struct VertexOutput { @builtin(position) clip_position: vec4, diff --git a/lyra-math/src/angle.rs b/lyra-math/src/angle.rs index 71ef507..30d541c 100755 --- a/lyra-math/src/angle.rs +++ b/lyra-math/src/angle.rs @@ -10,7 +10,7 @@ pub fn radians_to_degrees(radians: f32) -> f32 { radians * 180.0 / PI } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub enum Angle { Degrees(f32), Radians(f32), @@ -68,18 +68,4 @@ impl std::ops::SubAssign for Angle { Angle::Radians(r) => *r -= rhs.to_radians(), } } -} - -impl std::ops::Mul for Angle { - type Output = Angle; - - fn mul(self, rhs: f32) -> Self::Output { - Angle::Radians(self.to_radians() * rhs) - } -} - -impl std::ops::MulAssign for Angle { - fn mul_assign(&mut self, rhs: f32) { - *self = *self * rhs; - } } \ No newline at end of file