Implement Shadows #24

Merged
SeanOMik merged 28 commits from feat/shadow-maps into main 2024-08-10 03:10:30 +00:00
3 changed files with 59 additions and 16 deletions
Showing only changes of commit 27bc88c5a7 - Show all commits

View File

@ -115,9 +115,9 @@ impl Node for MeshPass {
.expect("missing ShadowMapsPassSlots::ShadowAtlasSampler") .expect("missing ShadowMapsPassSlots::ShadowAtlasSampler")
.as_sampler() .as_sampler()
.unwrap(); .unwrap();
let atlas_size_buf = graph let shadow_settings_buf = graph
.slot_value(ShadowMapsPassSlots::ShadowAtlasSizeBuffer) .slot_value(ShadowMapsPassSlots::ShadowSettingsUniform)
.expect("missing ShadowMapsPassSlots::ShadowAtlasSizeBuffer") .expect("missing ShadowMapsPassSlots::ShadowSettingsUniform")
.as_buffer() .as_buffer()
.unwrap(); .unwrap();
let light_uniform_buf = graph let light_uniform_buf = graph
@ -183,7 +183,7 @@ impl Node for MeshPass {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 2, binding: 2,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: atlas_size_buf, buffer: shadow_settings_buf,
offset: 0, offset: 0,
size: None, size: None,
}), }),

View File

@ -16,16 +16,12 @@ use tracing::warn;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use crate::render::{ use crate::render::{
graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue}, 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
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}; use super::{MeshBufferStorage, RenderAssets, RenderMeshes};
const PCF_SAMPLES_NUM: u32 = 4;
const SHADOW_SIZE: glam::UVec2 = glam::uvec2(1024, 1024); const SHADOW_SIZE: glam::UVec2 = glam::uvec2(1024, 1024);
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] #[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
@ -35,6 +31,7 @@ pub enum ShadowMapsPassSlots {
ShadowAtlasSampler, ShadowAtlasSampler,
ShadowAtlasSizeBuffer, ShadowAtlasSizeBuffer,
ShadowLightUniformsBuffer, ShadowLightUniformsBuffer,
ShadowSettingsUniform,
} }
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] #[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
@ -333,7 +330,7 @@ impl ShadowMapsPass {
impl Node for ShadowMapsPass { impl Node for ShadowMapsPass {
fn desc( fn desc(
&mut self, &mut self,
_: &mut crate::render::graph::RenderGraph, graph: &mut crate::render::graph::RenderGraph,
) -> crate::render::graph::NodeDesc { ) -> crate::render::graph::NodeDesc {
let mut node = NodeDesc::new(NodeType::Render, None, vec![]); 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())), 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::<ShadowSettingsUniform>() 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 node
} }
@ -380,6 +389,12 @@ impl Node for ShadowMapsPass {
world: &mut lyra_ecs::World, world: &mut lyra_ecs::World,
context: &mut crate::render::graph::RenderGraphContext, context: &mut crate::render::graph::RenderGraphContext,
) { ) {
{
// TODO: only write buffer on changes to resource
let shadow_settings = world.get_resource_or_default::<ShadowSettings>();
context.queue_buffer_write_with(ShadowMapsPassSlots::ShadowSettingsUniform, 0, ShadowSettingsUniform::from(*shadow_settings));
}
self.render_meshes = world.try_get_resource_data::<RenderMeshes>(); self.render_meshes = world.try_get_resource_data::<RenderMeshes>();
self.transform_buffers = world.try_get_resource_data::<TransformBuffers>(); self.transform_buffers = world.try_get_resource_data::<TransformBuffers>();
self.mesh_buffers = world.try_get_resource_data::<RenderAssets<MeshBufferStorage>>(); self.mesh_buffers = world.try_get_resource_data::<RenderAssets<MeshBufferStorage>>();
@ -691,3 +706,29 @@ impl LightShadowMapAtlas {
self.0.write().unwrap() 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<ShadowSettings> for ShadowSettingsUniform {
fn from(value: ShadowSettings) -> Self {
Self {
pcf_samples_num: value.pcf_samples_num,
}
}
}

View File

@ -8,8 +8,6 @@ const LIGHT_TY_SPOT = 2u;
const ALPHA_CUTOFF = 0.1; const ALPHA_CUTOFF = 0.1;
const SHADOW_MAP_PCF_SIZE = 4.0;
struct VertexInput { struct VertexInput {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>, @location(1) tex_coords: vec2<f32>,
@ -122,6 +120,10 @@ struct LightShadowMapUniform {
light_pos: vec3<f32>, light_pos: vec3<f32>,
} }
struct ShadowSettingsUniform {
pcf_samples_num: u32,
}
@group(4) @binding(0) @group(4) @binding(0)
var<storage, read_write> u_light_indices: array<u32>; var<storage, read_write> u_light_indices: array<u32>;
@group(4) @binding(1) @group(4) @binding(1)
@ -132,7 +134,7 @@ var t_shadow_maps_atlas: texture_depth_2d;
@group(5) @binding(1) @group(5) @binding(1)
var s_shadow_maps_atlas: sampler_comparison; var s_shadow_maps_atlas: sampler_comparison;
@group(5) @binding(2) @group(5) @binding(2)
var<uniform> u_shadow_maps_atlas_size: vec2<u32>; var<uniform> u_shadow_settings: ShadowSettingsUniform;
@group(5) @binding(3) @group(5) @binding(3)
var<storage, read> u_light_shadow: array<LightShadowMapUniform>; var<storage, read> u_light_shadow: array<LightShadowMapUniform>;
@ -280,7 +282,7 @@ fn calc_shadow_dir_light(normal: vec3<f32>, light_dir: vec3<f32>, frag_pos_light
/// Calculate the shadow coefficient using PCF of a directional light /// Calculate the shadow coefficient using PCF of a directional light
fn pcf_dir_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMapUniform) -> f32 { fn pcf_dir_light(tex_coords: vec2<f32>, 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>(f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height)); let texel_size = 1.0 / vec2<f32>(f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height));
// Sample PCF // Sample PCF
@ -292,7 +294,7 @@ fn pcf_dir_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMa
shadow += pcf_depth; 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 // ensure the shadow value does not go above 1.0
shadow = min(shadow, 1.0); shadow = min(shadow, 1.0);