diff --git a/lyra-game/src/render/graph/passes/meshes.rs b/lyra-game/src/render/graph/passes/meshes.rs index c0a315f..08aef81 100644 --- a/lyra-game/src/render/graph/passes/meshes.rs +++ b/lyra-game/src/render/graph/passes/meshes.rs @@ -115,9 +115,9 @@ impl Node for MeshPass { .expect("missing ShadowMapsPassSlots::ShadowAtlasSampler") .as_sampler() .unwrap(); - let atlas_size_buf = graph - .slot_value(ShadowMapsPassSlots::ShadowAtlasSizeBuffer) - .expect("missing ShadowMapsPassSlots::ShadowAtlasSizeBuffer") + let shadow_settings_buf = graph + .slot_value(ShadowMapsPassSlots::ShadowSettingsUniform) + .expect("missing ShadowMapsPassSlots::ShadowSettingsUniform") .as_buffer() .unwrap(); let light_uniform_buf = graph @@ -183,7 +183,7 @@ impl Node for MeshPass { wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer: atlas_size_buf, + buffer: shadow_settings_buf, offset: 0, size: None, }), diff --git a/lyra-game/src/render/graph/passes/shadows.rs b/lyra-game/src/render/graph/passes/shadows.rs index 15621bf..220d890 100644 --- a/lyra-game/src/render/graph/passes/shadows.rs +++ b/lyra-game/src/render/graph/passes/shadows.rs @@ -16,16 +16,12 @@ use tracing::warn; use wgpu::util::DeviceExt; use crate::render::{ - graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue}, - light::{directional::DirectionalLight, LightType, PointLight}, - resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, - transform_buffer_storage::TransformBuffers, - vertex::Vertex, - AtlasFrame, GpuSlotBuffer, TextureAtlas, + graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue}, light::{directional::DirectionalLight, LightType, PointLight}, resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, transform_buffer_storage::TransformBuffers, vertex::Vertex, AtlasFrame, GpuSlotBuffer, TextureAtlas }; use super::{MeshBufferStorage, RenderAssets, RenderMeshes}; +const PCF_SAMPLES_NUM: u32 = 4; const SHADOW_SIZE: glam::UVec2 = glam::uvec2(1024, 1024); #[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] @@ -35,6 +31,7 @@ pub enum ShadowMapsPassSlots { ShadowAtlasSampler, ShadowAtlasSizeBuffer, ShadowLightUniformsBuffer, + ShadowSettingsUniform, } #[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] @@ -333,7 +330,7 @@ impl ShadowMapsPass { impl Node for ShadowMapsPass { fn desc( &mut self, - _: &mut crate::render::graph::RenderGraph, + graph: &mut crate::render::graph::RenderGraph, ) -> crate::render::graph::NodeDesc { let mut node = NodeDesc::new(NodeType::Render, None, vec![]); @@ -371,6 +368,18 @@ impl Node for ShadowMapsPass { Some(SlotValue::Buffer(self.atlas_size_buffer.clone())), ); + let settings_buffer = graph.device().create_buffer(&wgpu::BufferDescriptor { + label: Some("buffer_shadow_settings"), + size: mem::size_of::() as _, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + node.add_buffer_slot( + ShadowMapsPassSlots::ShadowSettingsUniform, + SlotAttribute::Output, + Some(SlotValue::Buffer(Arc::new(settings_buffer))), + ); + node } @@ -380,6 +389,12 @@ impl Node for ShadowMapsPass { world: &mut lyra_ecs::World, context: &mut crate::render::graph::RenderGraphContext, ) { + { + // TODO: only write buffer on changes to resource + let shadow_settings = world.get_resource_or_default::(); + context.queue_buffer_write_with(ShadowMapsPassSlots::ShadowSettingsUniform, 0, ShadowSettingsUniform::from(*shadow_settings)); + } + self.render_meshes = world.try_get_resource_data::(); self.transform_buffers = world.try_get_resource_data::(); self.mesh_buffers = world.try_get_resource_data::>(); @@ -691,3 +706,29 @@ impl LightShadowMapAtlas { self.0.write().unwrap() } } + +#[derive(Debug, Copy, Clone)] +pub struct ShadowSettings { + pub pcf_samples_num: u32, +} + +impl Default for ShadowSettings { + fn default() -> Self { + Self { pcf_samples_num: PCF_SAMPLES_NUM } + } +} + +/// Uniform version of [`ShadowSettings`] +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct ShadowSettingsUniform { + pcf_samples_num: u32, +} + +impl From for ShadowSettingsUniform { + fn from(value: ShadowSettings) -> Self { + Self { + pcf_samples_num: value.pcf_samples_num, + } + } +} \ No newline at end of file diff --git a/lyra-game/src/render/shaders/base.wgsl b/lyra-game/src/render/shaders/base.wgsl index 1862ba0..4a61fa1 100755 --- a/lyra-game/src/render/shaders/base.wgsl +++ b/lyra-game/src/render/shaders/base.wgsl @@ -8,8 +8,6 @@ const LIGHT_TY_SPOT = 2u; const ALPHA_CUTOFF = 0.1; -const SHADOW_MAP_PCF_SIZE = 4.0; - struct VertexInput { @location(0) position: vec3, @location(1) tex_coords: vec2, @@ -122,6 +120,10 @@ struct LightShadowMapUniform { light_pos: vec3, } +struct ShadowSettingsUniform { + pcf_samples_num: u32, +} + @group(4) @binding(0) var u_light_indices: array; @group(4) @binding(1) @@ -132,7 +134,7 @@ var t_shadow_maps_atlas: texture_depth_2d; @group(5) @binding(1) var s_shadow_maps_atlas: sampler_comparison; @group(5) @binding(2) -var u_shadow_maps_atlas_size: vec2; +var u_shadow_settings: ShadowSettingsUniform; @group(5) @binding(3) var u_light_shadow: array; @@ -280,7 +282,7 @@ fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light /// Calculate the shadow coefficient using PCF of a directional light fn pcf_dir_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMapUniform) -> f32 { - let half_filter_size = SHADOW_MAP_PCF_SIZE / 2.0; + let half_filter_size = f32(u_shadow_settings.pcf_samples_num) / 2.0; let texel_size = 1.0 / vec2(f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height)); // Sample PCF @@ -292,7 +294,7 @@ fn pcf_dir_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMa shadow += pcf_depth; } } - shadow /= pow(SHADOW_MAP_PCF_SIZE, 2.0); + shadow /= pow(f32(u_shadow_settings.pcf_samples_num), 2.0); // ensure the shadow value does not go above 1.0 shadow = min(shadow, 1.0);