From 2b44d7f354780b81073ce12033d1aa407a29589c Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 14 Sep 2024 20:06:55 -0400 Subject: [PATCH] render: implement wgsl-preprocessor, split shaders --- .gitmodules | 3 + Cargo.lock | 76 +++- lyra-game/Cargo.toml | 1 + lyra-game/src/game.rs | 1 + lyra-game/src/render/graph/mod.rs | 18 + lyra-game/src/render/graph/passes/meshes.rs | 9 +- lyra-game/src/render/graph/passes/shadows.rs | 31 +- lyra-game/src/render/shaders/base.wgsl | 412 +----------------- .../shaders/shadows/shadows_bindings.wgsl | 19 + .../render/shaders/shadows/shadows_calc.wgsl | 352 +++++++++++++++ .../shadows_depth.wgsl} | 28 +- .../shaders/shadows/shadows_structs.wgsl | 29 ++ wgsl-preprocessor | 1 + 13 files changed, 541 insertions(+), 439 deletions(-) create mode 100644 lyra-game/src/render/shaders/shadows/shadows_bindings.wgsl create mode 100644 lyra-game/src/render/shaders/shadows/shadows_calc.wgsl rename lyra-game/src/render/shaders/{shadows.wgsl => shadows/shadows_depth.wgsl} (69%) create mode 100644 lyra-game/src/render/shaders/shadows/shadows_structs.wgsl create mode 160000 wgsl-preprocessor diff --git a/.gitmodules b/.gitmodules index d4916ae..1ff77f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lyra-scripting/elua"] path = lyra-scripting/elua url = ../elua.git # git@git.seanomik.net:SeanOMik/elua.git +[submodule "wgsl-preprocessor"] + path = wgsl-preprocessor + url = git@git.seanomik.net:SeanOMik/wgsl-preprocessor.git diff --git a/Cargo.lock b/Cargo.lock index bdf2688..c5318da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1996,6 +1996,7 @@ dependencies = [ "unique", "uuid", "wgpu", + "wgsl_preprocessor", "winit", ] @@ -2642,6 +2643,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "pest_meta" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "petgraph" version = "0.6.5" @@ -2917,9 +2963,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -3472,18 +3518,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -3776,6 +3822,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -4158,6 +4210,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wgsl_preprocessor" +version = "0.1.0" +dependencies = [ + "pest", + "pest_derive", + "regex", + "thiserror", + "tracing", + "tracing-subscriber", +] + [[package]] name = "widestring" version = "0.5.1" diff --git a/lyra-game/Cargo.toml b/lyra-game/Cargo.toml index 621a5f8..a9e125d 100644 --- a/lyra-game/Cargo.toml +++ b/lyra-game/Cargo.toml @@ -10,6 +10,7 @@ lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } lyra-math = { path = "../lyra-math" } lyra-scene = { path = "../lyra-scene" } +wgsl_preprocessor = { path = "../wgsl-preprocessor" } winit = "0.28.1" wgpu = { version = "0.15.1", features = [ "expose-ids"] } diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index 8ccf6c3..2ecf253 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -356,6 +356,7 @@ impl Game { t.with(filter::Targets::new() // done by prefix, so it includes all lyra subpackages .with_target("lyra", Level::DEBUG) + .with_target("wgsl_preprocessor", Level::DEBUG) .with_target("wgpu", Level::WARN) .with_target("winit", Level::DEBUG) .with_default(Level::INFO)) diff --git a/lyra-game/src/render/graph/mod.rs b/lyra-game/src/render/graph/mod.rs index 1b5ab96..0ca5f32 100644 --- a/lyra-game/src/render/graph/mod.rs +++ b/lyra-game/src/render/graph/mod.rs @@ -118,6 +118,7 @@ pub struct RenderGraph { /// A directed graph used to determine dependencies of nodes. node_graph: petgraph::matrix_graph::DiMatrix, usize>, view_target: Rc>, + shader_prepoc: wgsl_preprocessor::Processor, } impl RenderGraph { @@ -131,6 +132,7 @@ impl RenderGraph { bind_groups: Default::default(), node_graph: Default::default(), view_target, + shader_prepoc: wgsl_preprocessor::Processor::new(), } } @@ -510,6 +512,22 @@ impl RenderGraph { pub fn view_target_mut(&self) -> RefMut { self.view_target.borrow_mut() } + + /// Register a shader with the preprocessor. + /// + /// This step also parses the shader and will return errors if it failed to parse. + /// + /// Returns: The shader module import path if the module specified one. + #[inline(always)] + pub fn register_shader(&mut self, shader_src: &str) -> Result, wgsl_preprocessor::Error> { + self.shader_prepoc.parse_module(shader_src) + } + + /// Preprocess a shader, returning the source. + #[inline(always)] + pub fn preprocess_shader(&mut self, shader_path: &str) -> Result { + self.shader_prepoc.preprocess_module(shader_path) + } } pub struct SubGraphNode { diff --git a/lyra-game/src/render/graph/passes/meshes.rs b/lyra-game/src/render/graph/passes/meshes.rs index ba2858a..6c03544 100644 --- a/lyra-game/src/render/graph/passes/meshes.rs +++ b/lyra-game/src/render/graph/passes/meshes.rs @@ -102,6 +102,11 @@ impl Node for MeshPass { _: &mut RenderGraphContext, ) { if self.pipeline.is_none() { + let shader_mod = graph.register_shader(include_str!("../../shaders/base.wgsl")) + .expect("failed to register shader").expect("base shader missing module"); + let shader_src = graph.preprocess_shader(&shader_mod) + .expect("failed to preprocess shader"); + let device = graph.device(); let surface_config_format = graph.view_target().format(); @@ -295,8 +300,8 @@ impl Node for MeshPass { let atlas_bgl = self.shadows_atlas.as_ref().unwrap().layout.clone(); let shader = Rc::new(Shader { - label: Some("base_shader".into()), - source: include_str!("../../shaders/base.wgsl").to_string(), + label: Some(shader_mod.into()), + source: shader_src, }); let transforms = world diff --git a/lyra-game/src/render/graph/passes/shadows.rs b/lyra-game/src/render/graph/passes/shadows.rs index 3df7011..0f86ccd 100644 --- a/lyra-game/src/render/graph/passes/shadows.rs +++ b/lyra-game/src/render/graph/passes/shadows.rs @@ -19,7 +19,7 @@ use tracing::{debug, warn}; use wgpu::util::DeviceExt; use crate::render::{ - graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue}, + graph::{Node, NodeDesc, NodeType, RenderGraph, SlotAttribute, SlotValue}, light::{directional::DirectionalLight, LightType, PointLight, SpotLight}, resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, transform_buffer_storage::TransformBuffers, @@ -74,6 +74,7 @@ pub struct ShadowMapsPass { transform_buffers: Option, render_meshes: Option, mesh_buffers: Option, + shader: Option, pipeline: Option, point_light_pipeline: Option, @@ -170,6 +171,7 @@ impl ShadowMapsPass { transform_buffers: None, render_meshes: None, mesh_buffers: None, + shader: None, pipeline: None, point_light_pipeline: None, @@ -500,6 +502,23 @@ impl ShadowMapsPass { queue.write_buffer(buffer, 0, bytemuck::cast_slice(points.as_slice())); } + + /// Register all the shaders, returning the module of the + fn register_shaders(&self, graph: &mut RenderGraph) -> Result<(), wgsl_preprocessor::Error> { + let src = include_str!("../../shaders/shadows/shadows_structs.wgsl"); + graph.register_shader(src)?; + + let src = include_str!("../../shaders/shadows/shadows_bindings.wgsl"); + graph.register_shader(src)?; + + let src = include_str!("../../shaders/shadows/shadows_calc.wgsl"); + graph.register_shader(src)?; + + let src = include_str!("../../shaders/shadows/shadows_depth.wgsl"); + graph.register_shader(src)?; + + Ok(()) + } } impl Node for ShadowMapsPass { @@ -507,6 +526,12 @@ impl Node for ShadowMapsPass { &mut self, graph: &mut crate::render::graph::RenderGraph, ) -> crate::render::graph::NodeDesc { + self.register_shaders(graph) + .expect("failed to register shaders"); + self.shader = Some(graph.preprocess_shader("lyra::shadows::depth_pass") + .expect("failed to preprocess depth shadow shaders")); + println!("{}", self.shader.as_ref().unwrap()); + let mut node = NodeDesc::new(NodeType::Render, None, vec![]); let atlas = self.atlas.get(); @@ -747,8 +772,8 @@ impl Node for ShadowMapsPass { if self.pipeline.is_none() { let shader = Rc::new(Shader { - label: Some("shader_shadows".into()), - source: include_str!("../../shaders/shadows.wgsl").to_string(), + label: Some("lyra::shadows::depth_pass".into()), + source: self.shader.clone().unwrap(), }); let bgl = self.bgl.clone(); diff --git a/lyra-game/src/render/shaders/base.wgsl b/lyra-game/src/render/shaders/base.wgsl index 0b2f233..e0d73b9 100755 --- a/lyra-game/src/render/shaders/base.wgsl +++ b/lyra-game/src/render/shaders/base.wgsl @@ -1,6 +1,8 @@ -// Vertex shader +#define_module lyra::main_3d +#import lyra::shadows::bindings::{u_light_shadow} +#import lyra::shadows::calc::{calc_shadow_dir_light, calc_shadow_point_light, calc_shadow_spot_light} -const max_light_count: u32 = 16u; +// Vertex shader const LIGHT_TY_DIRECTIONAL = 0u; const LIGHT_TY_POINT = 1u; @@ -22,15 +24,6 @@ struct VertexOutput { @location(3) frag_pos_light_space: vec4, } -struct TextureAtlasFrame { - /*offset: vec2, - size: vec2,*/ - x: u32, - y: u32, - width: u32, - height: u32, -} - struct TransformData { transform: mat4x4, normal_matrix: mat4x4, @@ -43,7 +36,7 @@ struct CameraUniform { projection: mat4x4, position: vec3, tile_debug: u32, -}; +} struct Light { position: vec3, @@ -59,12 +52,12 @@ struct Light { spot_cutoff: f32, spot_outer_cutoff: f32, light_shadow_uniform_index: array, -}; +} struct Lights { light_count: u32, data: array, -}; +} @group(1) @binding(0) var u_model_transform_data: TransformData; @@ -112,47 +105,11 @@ var t_diffuse: texture_2d; @group(0) @binding(2) var s_diffuse: sampler; -struct LightShadowMapUniform { - light_space_matrix: mat4x4, - atlas_frame: TextureAtlasFrame, - near_plane: f32, - far_plane: f32, - light_size_uv: f32, - light_pos: vec3, - /// boolean casted as u32 - has_shadow_settings: u32, - pcf_samples_num: u32, - pcss_blocker_search_samples: u32, - constant_depth_bias: f32, -} - -struct ShadowSettingsUniform { - pcf_samples_num: u32, - pcss_blocker_search_samples: u32, -} - @group(4) @binding(0) var u_light_indices: array; @group(4) @binding(1) var t_light_grid: texture_storage_2d; // rg32uint = vec2 -@group(5) @binding(0) -var t_shadow_maps_atlas: texture_depth_2d; -@group(5) @binding(1) -var s_shadow_maps_atlas: sampler; -@group(5) @binding(2) -var s_shadow_maps_atlas_compare: sampler_comparison; -@group(5) @binding(3) -var u_shadow_settings: ShadowSettingsUniform; -@group(5) @binding(4) -var u_light_shadow: array; -@group(5) @binding(5) -var u_pcf_poisson_disc: array>; -@group(5) @binding(6) -var u_pcf_poisson_disc_3d: array>; -@group(5) @binding(7) -var u_pcss_poisson_disc: array>; - @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { if (u_camera.tile_debug == 1u) { @@ -173,8 +130,6 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let light_offset = tile.x; let light_count = tile.y; - let atlas_dimensions = textureDimensions(t_shadow_maps_atlas); - for (var i = 0u; i < light_count; i++) { let light_index = u_light_indices[light_offset + i]; let light: Light = u_lights.data[light_index]; @@ -187,10 +142,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { 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); + let shadow = calc_shadow_point_light(in.world_position, in.world_normal, light_dir, light); 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, atlas_dimensions); + 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); } } @@ -199,355 +154,6 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { return vec4(light_object_res, object_color.a); } -/// Convert 3d coords for an unwrapped cubemap to 2d coords and a side index of the cube map. -/// -/// The `xy` components are the 2d coordinates in the side of the cube, and `z` is the cube -/// map side index. -/// -/// Cube map index results: -/// 0 -> UNKNOWN -/// 1 -> right -/// 2 -> left -/// 3 -> top -/// 4 -> bottom -/// 5 -> near -/// 6 -> far -fn coords_to_cube_atlas(tex_coord: vec3) -> vec3 { - let abs_x = abs(tex_coord.x); - let abs_y = abs(tex_coord.y); - let abs_z = abs(tex_coord.z); - - var major_axis: f32 = 0.0; - var cube_idx: i32 = 0; - var res = vec2(0.0); - - // Determine the dominant axis - if (abs_x >= abs_y && abs_x >= abs_z) { - major_axis = tex_coord.x; - if (tex_coord.x > 0.0) { - cube_idx = 1; - res = vec2(-tex_coord.z, -tex_coord.y); - } else { - cube_idx = 2; - res = vec2(tex_coord.z, -tex_coord.y); - } - } else if (abs_y >= abs_x && abs_y >= abs_z) { - major_axis = tex_coord.y; - if (tex_coord.y > 0.0) { - cube_idx = 3; - res = vec2(tex_coord.x, tex_coord.z); - } else { - cube_idx = 4; - res = vec2(tex_coord.x, -tex_coord.z); - } - } else { - major_axis = tex_coord.z; - if (tex_coord.z > 0.0) { - cube_idx = 5; - res = vec2(tex_coord.x, -tex_coord.y); - } else { - cube_idx = 6; - res = vec2(-tex_coord.x, -tex_coord.y); - } - } - - res = (res / abs(major_axis) + 1.0) * 0.5; - res.y = 1.0 - res.y; - - return vec3(res, f32(cube_idx)); -} - -/// Get shadow settings for a light. -/// Returns x as `pcf_samples_num` and y as `pcss_blocker_search_samples`. -fn get_shadow_settings(shadow_u: LightShadowMapUniform) -> vec2 { - if shadow_u.has_shadow_settings == 1u { - return vec2(shadow_u.pcf_samples_num, shadow_u.pcss_blocker_search_samples); - } else { - return vec2(u_shadow_settings.pcf_samples_num, u_shadow_settings.pcss_blocker_search_samples); - } -} - -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; - - // 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); - } - // PCSS - else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u { - 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, 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; -} - -// Comes from https://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf -fn search_width(light_near: f32, uv_light_size: f32, receiver_depth: f32) -> f32 { - return uv_light_size * (receiver_depth - light_near) / receiver_depth; -} - -/// Convert texture coords to be texture coords of an atlas frame. -/// -/// 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 - var region_rect = vec4(f32(shadow_u.atlas_frame.x), f32(shadow_u.atlas_frame.y), - f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height)); - // put the frame rect in atlas UV space - region_rect /= f32(atlas_dimensions.x); - - // 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( - mix(region_rect.x + texel_size, region_rect.x + region_rect.z - texel_size, coords.x), - mix(region_rect.y + texel_size, region_rect.y + region_rect.w - texel_size, coords.y) - ); - - return region_coords; -} - -/// Find the average blocker distance for a directiona llight -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 < 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); - - if z < (receiver_depth - bias) { - blockers += 1; - avg_dist += z; - } - } - - let b = f32(blockers); - return vec2(avg_dist / b, b); -} - -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 { - return 1.0; - } - let blocker_depth = blocker_search.x; - - // penumbra estimation - let penumbra_width = (receiver_depth - blocker_depth) / blocker_depth; - - // 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, 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, 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); - - shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); - } - shadow /= f32(samples_num); - - // clamp shadow to [0; 1] - return saturate(shadow); -} - -fn calc_shadow_point_light(world_pos: vec3, world_normal: vec3, light_dir: vec3, light: Light, atlas_dimensions: vec2) -> f32 { - var frag_to_light = world_pos - light.position; - let temp = coords_to_cube_atlas(normalize(frag_to_light)); - var coords_2d = temp.xy; - let cube_idx = i32(temp.z); - - var indices = light.light_shadow_uniform_index; - let i = indices[cube_idx - 1]; - let u: LightShadowMapUniform = u_light_shadow[i]; - - let uniforms = array( - u_light_shadow[indices[0]], - u_light_shadow[indices[1]], - u_light_shadow[indices[2]], - u_light_shadow[indices[3]], - u_light_shadow[indices[4]], - u_light_shadow[indices[5]] - ); - - var current_depth = length(frag_to_light); - current_depth /= u.far_plane; - current_depth -= u.constant_depth_bias; - - // get settings - let settings = get_shadow_settings(u); - 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(u, coords_2d, true); - 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(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, 0.007); - //shadow = pcf_point_light(coords_2d, current_depth, u, pcf_samples_num, texel_size); - } - // no filtering - else { - 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); - } - - return shadow; -} - -/// Calculate the shadow coefficient using PCF of a directional light -fn pcf_point_light(tex_coords: vec3, test_depth: f32, shadow_us: array, samples_num: u32, uv_radius: f32) -> f32 { - var shadow_unis = shadow_us; - - var shadow = 0.0; - for (var i = 0; i < i32(samples_num); i++) { - var temp = coords_to_cube_atlas(tex_coords); - var coords_2d = temp.xy; - var cube_idx = i32(temp.z); - var shadow_u = shadow_unis[cube_idx - 1]; - - coords_2d += u_pcf_poisson_disc[i] * uv_radius; - - 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 /= f32(samples_num); - - // clamp shadow to [0; 1] - return saturate(shadow); -} - -fn calc_shadow_spot_light(world_pos: vec3, world_normal: vec3, light_dir: vec3, light: Light, atlas_dimensions: vec2) -> 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; -} - -/// Calculate the shadow coefficient using PCF of a directional light -fn pcf_spot_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: i32, uv_radius: f32) -> f32 { - var shadow = 0.0; - 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); - - shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); - } - shadow /= f32(samples_num); - - // clamp shadow to [0; 1] - return saturate(shadow); -} - fn debug_grid(in: VertexOutput) -> vec4 { let tile_index_float: vec2 = in.clip_position.xy / 16.0; let tile_index = vec2(floor(tile_index_float)); diff --git a/lyra-game/src/render/shaders/shadows/shadows_bindings.wgsl b/lyra-game/src/render/shaders/shadows/shadows_bindings.wgsl new file mode 100644 index 0000000..f944011 --- /dev/null +++ b/lyra-game/src/render/shaders/shadows/shadows_bindings.wgsl @@ -0,0 +1,19 @@ +#define_module lyra::shadows::bindings +#import lyra::shadows::structs::{ShadowSettingsUniform, LightShadowMapUniform} + +@group(5) @binding(0) +var t_shadow_maps_atlas: texture_depth_2d; +@group(5) @binding(1) +var s_shadow_maps_atlas: sampler; +@group(5) @binding(2) +var s_shadow_maps_atlas_compare: sampler_comparison; +@group(5) @binding(3) +var u_shadow_settings: ShadowSettingsUniform; +@group(5) @binding(4) +var u_light_shadow: array; +@group(5) @binding(5) +var u_pcf_poisson_disc: array>; +@group(5) @binding(6) +var u_pcf_poisson_disc_3d: array>; +@group(5) @binding(7) +var u_pcss_poisson_disc: array>; \ No newline at end of file diff --git a/lyra-game/src/render/shaders/shadows/shadows_calc.wgsl b/lyra-game/src/render/shaders/shadows/shadows_calc.wgsl new file mode 100644 index 0000000..16d0ec0 --- /dev/null +++ b/lyra-game/src/render/shaders/shadows/shadows_calc.wgsl @@ -0,0 +1,352 @@ +#define_module lyra::shadows::calc +#import lyra::shadows::structs::{ShadowSettingsUniform, LightShadowMapUniform} +#import lyra::shadows::bindings::{t_shadow_maps_atlas, s_shadow_maps_atlas, s_shadow_maps_atlas_compare, u_shadow_settings, u_light_shadow, u_pcf_poisson_disc, u_pcss_poisson_disc} + +/// Convert 3d coords for an unwrapped cubemap to 2d coords and a side index of the cube map. +/// +/// The `xy` components are the 2d coordinates in the side of the cube, and `z` is the cube +/// map side index. +/// +/// Cube map index results: +/// 0 -> UNKNOWN +/// 1 -> right +/// 2 -> left +/// 3 -> top +/// 4 -> bottom +/// 5 -> near +/// 6 -> far +fn coords_to_cube_atlas(tex_coord: vec3) -> vec3 { + let abs_x = abs(tex_coord.x); + let abs_y = abs(tex_coord.y); + let abs_z = abs(tex_coord.z); + + var major_axis: f32 = 0.0; + var cube_idx: i32 = 0; + var res = vec2(0.0); + + // Determine the dominant axis + if (abs_x >= abs_y && abs_x >= abs_z) { + major_axis = tex_coord.x; + if (tex_coord.x > 0.0) { + cube_idx = 1; + res = vec2(-tex_coord.z, -tex_coord.y); + } else { + cube_idx = 2; + res = vec2(tex_coord.z, -tex_coord.y); + } + } else if (abs_y >= abs_x && abs_y >= abs_z) { + major_axis = tex_coord.y; + if (tex_coord.y > 0.0) { + cube_idx = 3; + res = vec2(tex_coord.x, tex_coord.z); + } else { + cube_idx = 4; + res = vec2(tex_coord.x, -tex_coord.z); + } + } else { + major_axis = tex_coord.z; + if (tex_coord.z > 0.0) { + cube_idx = 5; + res = vec2(tex_coord.x, -tex_coord.y); + } else { + cube_idx = 6; + res = vec2(-tex_coord.x, -tex_coord.y); + } + } + + res = (res / abs(major_axis) + 1.0) * 0.5; + res.y = 1.0 - res.y; + + return vec3(res, f32(cube_idx)); +} + +/// Get shadow settings for a light. +/// Returns x as `pcf_samples_num` and y as `pcss_blocker_search_samples`. +fn get_shadow_settings(shadow_u: LightShadowMapUniform) -> vec2 { + if shadow_u.has_shadow_settings == 1u { + return vec2(shadow_u.pcf_samples_num, shadow_u.pcss_blocker_search_samples); + } else { + return vec2(u_shadow_settings.pcf_samples_num, u_shadow_settings.pcss_blocker_search_samples); + } +} + +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; + + // 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); + } + // PCSS + else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u { + 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, 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; +} + +// Comes from https://developer.download.nvidia.com/whitepapers/2008/PCSS_Integration.pdf +fn search_width(light_near: f32, uv_light_size: f32, receiver_depth: f32) -> f32 { + return uv_light_size * (receiver_depth - light_near) / receiver_depth; +} + +/// Convert texture coords to be texture coords of an atlas frame. +/// +/// 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 + var region_rect = vec4(f32(shadow_u.atlas_frame.x), f32(shadow_u.atlas_frame.y), + f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height)); + // put the frame rect in atlas UV space + region_rect /= f32(atlas_dimensions.x); + + // 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( + mix(region_rect.x + texel_size, region_rect.x + region_rect.z - texel_size, coords.x), + mix(region_rect.y + texel_size, region_rect.y + region_rect.w - texel_size, coords.y) + ); + + return region_coords; +} + +/// Find the average blocker distance for a directiona llight +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 < 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); + + if z < (receiver_depth - bias) { + blockers += 1; + avg_dist += z; + } + } + + let b = f32(blockers); + return vec2(avg_dist / b, b); +} + +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 { + return 1.0; + } + let blocker_depth = blocker_search.x; + + // penumbra estimation + let penumbra_width = (receiver_depth - blocker_depth) / blocker_depth; + + // 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, 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, 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); + + shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); + } + shadow /= f32(samples_num); + + // clamp shadow to [0; 1] + return saturate(shadow); +} + +fn calc_shadow_point_light(world_pos: vec3, world_normal: vec3, light_dir: vec3, light: Light) -> f32 { + var frag_to_light = world_pos - light.position; + let temp = coords_to_cube_atlas(normalize(frag_to_light)); + var coords_2d = temp.xy; + let cube_idx = i32(temp.z); + + var indices = light.light_shadow_uniform_index; + let i = indices[cube_idx - 1]; + let u: LightShadowMapUniform = u_light_shadow[i]; + + let uniforms = array( + u_light_shadow[indices[0]], + u_light_shadow[indices[1]], + u_light_shadow[indices[2]], + u_light_shadow[indices[3]], + u_light_shadow[indices[4]], + u_light_shadow[indices[5]] + ); + + var current_depth = length(frag_to_light); + current_depth /= u.far_plane; + current_depth -= u.constant_depth_bias; + + // get settings + let settings = get_shadow_settings(u); + 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(u, coords_2d, true); + 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(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, 0.007); + //shadow = pcf_point_light(coords_2d, current_depth, u, pcf_samples_num, texel_size); + } + // no filtering + else { + 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); + } + + return shadow; +} + +/// Calculate the shadow coefficient using PCF of a directional light +fn pcf_point_light(tex_coords: vec3, test_depth: f32, shadow_us: array, samples_num: u32, uv_radius: f32) -> f32 { + var shadow_unis = shadow_us; + + var shadow = 0.0; + for (var i = 0; i < i32(samples_num); i++) { + var temp = coords_to_cube_atlas(tex_coords); + var coords_2d = temp.xy; + var cube_idx = i32(temp.z); + var shadow_u = shadow_unis[cube_idx - 1]; + + coords_2d += u_pcf_poisson_disc[i] * uv_radius; + + 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 /= f32(samples_num); + + // 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; +} + +/// Calculate the shadow coefficient using PCF of a directional light +fn pcf_spot_light(tex_coords: vec2, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: i32, uv_radius: f32) -> f32 { + var shadow = 0.0; + 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); + + shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth); + } + shadow /= f32(samples_num); + + // clamp shadow to [0; 1] + return saturate(shadow); +} \ No newline at end of file diff --git a/lyra-game/src/render/shaders/shadows.wgsl b/lyra-game/src/render/shaders/shadows/shadows_depth.wgsl similarity index 69% rename from lyra-game/src/render/shaders/shadows.wgsl rename to lyra-game/src/render/shaders/shadows/shadows_depth.wgsl index 58be2c5..82013a1 100644 --- a/lyra-game/src/render/shaders/shadows.wgsl +++ b/lyra-game/src/render/shaders/shadows/shadows_depth.wgsl @@ -1,38 +1,16 @@ +#define_module lyra::shadows::depth_pass +#import lyra::shadows::structs::{LightShadowMapUniform} + struct TransformData { transform: mat4x4, normal_matrix: mat4x4, } -struct TextureAtlasFrame { - offset: vec2, - size: vec2, -} - -struct LightShadowMapUniform { - light_space_matrix: mat4x4, - atlas_frame: TextureAtlasFrame, - near_plane: f32, - far_plane: f32, - light_size_uv: f32, - light_pos: vec3, - /// boolean casted as u32 - has_shadow_settings: u32, - pcf_samples_num: u32, - pcss_blocker_search_samples: u32, - constant_depth_bias: f32, -} - @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-game/src/render/shaders/shadows/shadows_structs.wgsl b/lyra-game/src/render/shaders/shadows/shadows_structs.wgsl new file mode 100644 index 0000000..bdc85ac --- /dev/null +++ b/lyra-game/src/render/shaders/shadows/shadows_structs.wgsl @@ -0,0 +1,29 @@ +#define_module lyra::shadows::structs + +struct TextureAtlasFrame { + /*offset: vec2, + size: vec2,*/ + x: u32, + y: u32, + width: u32, + height: u32, +} + +struct LightShadowMapUniform { + light_space_matrix: mat4x4, + atlas_frame: TextureAtlasFrame, + near_plane: f32, + far_plane: f32, + light_size_uv: f32, + light_pos: vec3, + /// boolean casted as u32 + has_shadow_settings: u32, + pcf_samples_num: u32, + pcss_blocker_search_samples: u32, + constant_depth_bias: f32, +} + +struct ShadowSettingsUniform { + pcf_samples_num: u32, + pcss_blocker_search_samples: u32, +} \ No newline at end of file diff --git a/wgsl-preprocessor b/wgsl-preprocessor new file mode 160000 index 0000000..70daf32 --- /dev/null +++ b/wgsl-preprocessor @@ -0,0 +1 @@ +Subproject commit 70daf320827f64b325a77718df07177d74d7ea58