render: implement wgsl-preprocessor, split shaders
CI / build (push) Failing after 7m56s
Details
CI / build (push) Failing after 7m56s
Details
This commit is contained in:
parent
60c139f9b2
commit
2b44d7f354
|
@ -1,3 +1,6 @@
|
||||||
[submodule "lyra-scripting/elua"]
|
[submodule "lyra-scripting/elua"]
|
||||||
path = lyra-scripting/elua
|
path = lyra-scripting/elua
|
||||||
url = ../elua.git # git@git.seanomik.net:SeanOMik/elua.git
|
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
|
||||||
|
|
|
@ -1996,6 +1996,7 @@ dependencies = [
|
||||||
"unique",
|
"unique",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
"wgsl_preprocessor",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2642,6 +2643,51 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
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]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
@ -2917,9 +2963,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.4"
|
version = "1.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -3472,18 +3518,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.56"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.56"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3776,6 +3822,12 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
|
@ -4158,6 +4210,18 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgsl_preprocessor"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"regex",
|
||||||
|
"thiserror",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
|
@ -10,6 +10,7 @@ lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
||||||
lyra-math = { path = "../lyra-math" }
|
lyra-math = { path = "../lyra-math" }
|
||||||
lyra-scene = { path = "../lyra-scene" }
|
lyra-scene = { path = "../lyra-scene" }
|
||||||
|
wgsl_preprocessor = { path = "../wgsl-preprocessor" }
|
||||||
|
|
||||||
winit = "0.28.1"
|
winit = "0.28.1"
|
||||||
wgpu = { version = "0.15.1", features = [ "expose-ids"] }
|
wgpu = { version = "0.15.1", features = [ "expose-ids"] }
|
||||||
|
|
|
@ -356,6 +356,7 @@ impl Game {
|
||||||
t.with(filter::Targets::new()
|
t.with(filter::Targets::new()
|
||||||
// done by prefix, so it includes all lyra subpackages
|
// done by prefix, so it includes all lyra subpackages
|
||||||
.with_target("lyra", Level::DEBUG)
|
.with_target("lyra", Level::DEBUG)
|
||||||
|
.with_target("wgsl_preprocessor", Level::DEBUG)
|
||||||
.with_target("wgpu", Level::WARN)
|
.with_target("wgpu", Level::WARN)
|
||||||
.with_target("winit", Level::DEBUG)
|
.with_target("winit", Level::DEBUG)
|
||||||
.with_default(Level::INFO))
|
.with_default(Level::INFO))
|
||||||
|
|
|
@ -118,6 +118,7 @@ pub struct RenderGraph {
|
||||||
/// A directed graph used to determine dependencies of nodes.
|
/// A directed graph used to determine dependencies of nodes.
|
||||||
node_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
|
node_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
|
||||||
view_target: Rc<RefCell<ViewTarget>>,
|
view_target: Rc<RefCell<ViewTarget>>,
|
||||||
|
shader_prepoc: wgsl_preprocessor::Processor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
|
@ -131,6 +132,7 @@ impl RenderGraph {
|
||||||
bind_groups: Default::default(),
|
bind_groups: Default::default(),
|
||||||
node_graph: Default::default(),
|
node_graph: Default::default(),
|
||||||
view_target,
|
view_target,
|
||||||
|
shader_prepoc: wgsl_preprocessor::Processor::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,6 +512,22 @@ impl RenderGraph {
|
||||||
pub fn view_target_mut(&self) -> RefMut<ViewTarget> {
|
pub fn view_target_mut(&self) -> RefMut<ViewTarget> {
|
||||||
self.view_target.borrow_mut()
|
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<Option<String>, 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<String, wgsl_preprocessor::Error> {
|
||||||
|
self.shader_prepoc.preprocess_module(shader_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubGraphNode {
|
pub struct SubGraphNode {
|
||||||
|
|
|
@ -102,6 +102,11 @@ impl Node for MeshPass {
|
||||||
_: &mut RenderGraphContext,
|
_: &mut RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
if self.pipeline.is_none() {
|
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 device = graph.device();
|
||||||
let surface_config_format = graph.view_target().format();
|
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 atlas_bgl = self.shadows_atlas.as_ref().unwrap().layout.clone();
|
||||||
|
|
||||||
let shader = Rc::new(Shader {
|
let shader = Rc::new(Shader {
|
||||||
label: Some("base_shader".into()),
|
label: Some(shader_mod.into()),
|
||||||
source: include_str!("../../shaders/base.wgsl").to_string(),
|
source: shader_src,
|
||||||
});
|
});
|
||||||
|
|
||||||
let transforms = world
|
let transforms = world
|
||||||
|
|
|
@ -19,7 +19,7 @@ use tracing::{debug, 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, RenderGraph, SlotAttribute, SlotValue},
|
||||||
light::{directional::DirectionalLight, LightType, PointLight, SpotLight},
|
light::{directional::DirectionalLight, LightType, PointLight, SpotLight},
|
||||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
||||||
transform_buffer_storage::TransformBuffers,
|
transform_buffer_storage::TransformBuffers,
|
||||||
|
@ -74,6 +74,7 @@ pub struct ShadowMapsPass {
|
||||||
transform_buffers: Option<ResourceData>,
|
transform_buffers: Option<ResourceData>,
|
||||||
render_meshes: Option<ResourceData>,
|
render_meshes: Option<ResourceData>,
|
||||||
mesh_buffers: Option<ResourceData>,
|
mesh_buffers: Option<ResourceData>,
|
||||||
|
shader: Option<String>,
|
||||||
pipeline: Option<RenderPipeline>,
|
pipeline: Option<RenderPipeline>,
|
||||||
point_light_pipeline: Option<RenderPipeline>,
|
point_light_pipeline: Option<RenderPipeline>,
|
||||||
|
|
||||||
|
@ -170,6 +171,7 @@ impl ShadowMapsPass {
|
||||||
transform_buffers: None,
|
transform_buffers: None,
|
||||||
render_meshes: None,
|
render_meshes: None,
|
||||||
mesh_buffers: None,
|
mesh_buffers: None,
|
||||||
|
shader: None,
|
||||||
pipeline: None,
|
pipeline: None,
|
||||||
point_light_pipeline: None,
|
point_light_pipeline: None,
|
||||||
|
|
||||||
|
@ -500,6 +502,23 @@ impl ShadowMapsPass {
|
||||||
|
|
||||||
queue.write_buffer(buffer, 0, bytemuck::cast_slice(points.as_slice()));
|
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 {
|
impl Node for ShadowMapsPass {
|
||||||
|
@ -507,6 +526,12 @@ impl Node for ShadowMapsPass {
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::NodeDesc {
|
) -> 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 mut node = NodeDesc::new(NodeType::Render, None, vec![]);
|
||||||
|
|
||||||
let atlas = self.atlas.get();
|
let atlas = self.atlas.get();
|
||||||
|
@ -747,8 +772,8 @@ impl Node for ShadowMapsPass {
|
||||||
|
|
||||||
if self.pipeline.is_none() {
|
if self.pipeline.is_none() {
|
||||||
let shader = Rc::new(Shader {
|
let shader = Rc::new(Shader {
|
||||||
label: Some("shader_shadows".into()),
|
label: Some("lyra::shadows::depth_pass".into()),
|
||||||
source: include_str!("../../shaders/shadows.wgsl").to_string(),
|
source: self.shader.clone().unwrap(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let bgl = self.bgl.clone();
|
let bgl = self.bgl.clone();
|
||||||
|
|
|
@ -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_DIRECTIONAL = 0u;
|
||||||
const LIGHT_TY_POINT = 1u;
|
const LIGHT_TY_POINT = 1u;
|
||||||
|
@ -22,15 +24,6 @@ struct VertexOutput {
|
||||||
@location(3) frag_pos_light_space: vec4<f32>,
|
@location(3) frag_pos_light_space: vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextureAtlasFrame {
|
|
||||||
/*offset: vec2<u32>,
|
|
||||||
size: vec2<u32>,*/
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TransformData {
|
struct TransformData {
|
||||||
transform: mat4x4<f32>,
|
transform: mat4x4<f32>,
|
||||||
normal_matrix: mat4x4<f32>,
|
normal_matrix: mat4x4<f32>,
|
||||||
|
@ -43,7 +36,7 @@ struct CameraUniform {
|
||||||
projection: mat4x4<f32>,
|
projection: mat4x4<f32>,
|
||||||
position: vec3<f32>,
|
position: vec3<f32>,
|
||||||
tile_debug: u32,
|
tile_debug: u32,
|
||||||
};
|
}
|
||||||
|
|
||||||
struct Light {
|
struct Light {
|
||||||
position: vec3<f32>,
|
position: vec3<f32>,
|
||||||
|
@ -59,12 +52,12 @@ struct Light {
|
||||||
spot_cutoff: f32,
|
spot_cutoff: f32,
|
||||||
spot_outer_cutoff: f32,
|
spot_outer_cutoff: f32,
|
||||||
light_shadow_uniform_index: array<i32, 6>,
|
light_shadow_uniform_index: array<i32, 6>,
|
||||||
};
|
}
|
||||||
|
|
||||||
struct Lights {
|
struct Lights {
|
||||||
light_count: u32,
|
light_count: u32,
|
||||||
data: array<Light>,
|
data: array<Light>,
|
||||||
};
|
}
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> u_model_transform_data: TransformData;
|
var<uniform> u_model_transform_data: TransformData;
|
||||||
|
@ -112,47 +105,11 @@ var t_diffuse: texture_2d<f32>;
|
||||||
@group(0) @binding(2)
|
@group(0) @binding(2)
|
||||||
var s_diffuse: sampler;
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
struct LightShadowMapUniform {
|
|
||||||
light_space_matrix: mat4x4<f32>,
|
|
||||||
atlas_frame: TextureAtlasFrame,
|
|
||||||
near_plane: f32,
|
|
||||||
far_plane: f32,
|
|
||||||
light_size_uv: f32,
|
|
||||||
light_pos: vec3<f32>,
|
|
||||||
/// 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)
|
@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)
|
||||||
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // rg32uint = vec2<u32>
|
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // rg32uint = vec2<u32>
|
||||||
|
|
||||||
@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<uniform> u_shadow_settings: ShadowSettingsUniform;
|
|
||||||
@group(5) @binding(4)
|
|
||||||
var<storage, read> u_light_shadow: array<LightShadowMapUniform>;
|
|
||||||
@group(5) @binding(5)
|
|
||||||
var<storage, read> u_pcf_poisson_disc: array<vec2<f32>>;
|
|
||||||
@group(5) @binding(6)
|
|
||||||
var<storage, read> u_pcf_poisson_disc_3d: array<vec3<f32>>;
|
|
||||||
@group(5) @binding(7)
|
|
||||||
var<storage, read> u_pcss_poisson_disc: array<vec2<f32>>;
|
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
if (u_camera.tile_debug == 1u) {
|
if (u_camera.tile_debug == 1u) {
|
||||||
|
@ -173,8 +130,6 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let light_offset = tile.x;
|
let light_offset = tile.x;
|
||||||
let light_count = tile.y;
|
let light_count = tile.y;
|
||||||
|
|
||||||
let atlas_dimensions = textureDimensions(t_shadow_maps_atlas);
|
|
||||||
|
|
||||||
for (var i = 0u; i < light_count; i++) {
|
for (var i = 0u; i < light_count; i++) {
|
||||||
let light_index = u_light_indices[light_offset + i];
|
let light_index = u_light_indices[light_offset + i];
|
||||||
let light: Light = u_lights.data[light_index];
|
let light: Light = u_lights.data[light_index];
|
||||||
|
@ -187,10 +142,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let shadow = calc_shadow_dir_light(in.world_position, in.world_normal, light_dir, light);
|
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);
|
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) {
|
} 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);
|
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) {
|
} 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);
|
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<f32> {
|
||||||
return vec4<f32>(light_object_res, object_color.a);
|
return vec4<f32>(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<f32>) -> vec3<f32> {
|
|
||||||
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<f32>(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<f32>(-tex_coord.z, -tex_coord.y);
|
|
||||||
} else {
|
|
||||||
cube_idx = 2;
|
|
||||||
res = vec2<f32>(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<f32>(tex_coord.x, tex_coord.z);
|
|
||||||
} else {
|
|
||||||
cube_idx = 4;
|
|
||||||
res = vec2<f32>(tex_coord.x, -tex_coord.z);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
major_axis = tex_coord.z;
|
|
||||||
if (tex_coord.z > 0.0) {
|
|
||||||
cube_idx = 5;
|
|
||||||
res = vec2<f32>(tex_coord.x, -tex_coord.y);
|
|
||||||
} else {
|
|
||||||
cube_idx = 6;
|
|
||||||
res = vec2<f32>(-tex_coord.x, -tex_coord.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res = (res / abs(major_axis) + 1.0) * 0.5;
|
|
||||||
res.y = 1.0 - res.y;
|
|
||||||
|
|
||||||
return vec3<f32>(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<u32> {
|
|
||||||
if shadow_u.has_shadow_settings == 1u {
|
|
||||||
return vec2<u32>(shadow_u.pcf_samples_num, shadow_u.pcss_blocker_search_samples);
|
|
||||||
} else {
|
|
||||||
return vec2<u32>(u_shadow_settings.pcf_samples_num, u_shadow_settings.pcss_blocker_search_samples);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_shadow_dir_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, 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<f32>(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<f32>, safety_offset: bool) -> vec2<f32> {
|
|
||||||
let atlas_dimensions = textureDimensions(t_shadow_maps_atlas);
|
|
||||||
|
|
||||||
// get the rect of the frame as a vec4
|
|
||||||
var region_rect = vec4<f32>(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<f32>(
|
|
||||||
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<f32>, receiver_depth: f32, bias: f32, shadow_u: LightShadowMapUniform) -> vec2<f32> {
|
|
||||||
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<f32>(avg_dist / b, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pcss_dir_light(tex_coords: vec2<f32>, 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<f32>, 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<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, light: Light, atlas_dimensions: vec2<i32>) -> 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<LightShadowMapUniform, 6>(
|
|
||||||
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<f32>, test_depth: f32, shadow_us: array<LightShadowMapUniform, 6>, 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<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, light: Light, atlas_dimensions: vec2<i32>) -> 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<f32>(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<f32>, 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<f32> {
|
fn debug_grid(in: VertexOutput) -> vec4<f32> {
|
||||||
let tile_index_float: vec2<f32> = in.clip_position.xy / 16.0;
|
let tile_index_float: vec2<f32> = in.clip_position.xy / 16.0;
|
||||||
let tile_index = vec2<u32>(floor(tile_index_float));
|
let tile_index = vec2<u32>(floor(tile_index_float));
|
||||||
|
|
|
@ -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<uniform> u_shadow_settings: ShadowSettingsUniform;
|
||||||
|
@group(5) @binding(4)
|
||||||
|
var<storage, read> u_light_shadow: array<LightShadowMapUniform>;
|
||||||
|
@group(5) @binding(5)
|
||||||
|
var<storage, read> u_pcf_poisson_disc: array<vec2<f32>>;
|
||||||
|
@group(5) @binding(6)
|
||||||
|
var<storage, read> u_pcf_poisson_disc_3d: array<vec3<f32>>;
|
||||||
|
@group(5) @binding(7)
|
||||||
|
var<storage, read> u_pcss_poisson_disc: array<vec2<f32>>;
|
|
@ -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<f32>) -> vec3<f32> {
|
||||||
|
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<f32>(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<f32>(-tex_coord.z, -tex_coord.y);
|
||||||
|
} else {
|
||||||
|
cube_idx = 2;
|
||||||
|
res = vec2<f32>(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<f32>(tex_coord.x, tex_coord.z);
|
||||||
|
} else {
|
||||||
|
cube_idx = 4;
|
||||||
|
res = vec2<f32>(tex_coord.x, -tex_coord.z);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
major_axis = tex_coord.z;
|
||||||
|
if (tex_coord.z > 0.0) {
|
||||||
|
cube_idx = 5;
|
||||||
|
res = vec2<f32>(tex_coord.x, -tex_coord.y);
|
||||||
|
} else {
|
||||||
|
cube_idx = 6;
|
||||||
|
res = vec2<f32>(-tex_coord.x, -tex_coord.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = (res / abs(major_axis) + 1.0) * 0.5;
|
||||||
|
res.y = 1.0 - res.y;
|
||||||
|
|
||||||
|
return vec3<f32>(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<u32> {
|
||||||
|
if shadow_u.has_shadow_settings == 1u {
|
||||||
|
return vec2<u32>(shadow_u.pcf_samples_num, shadow_u.pcss_blocker_search_samples);
|
||||||
|
} else {
|
||||||
|
return vec2<u32>(u_shadow_settings.pcf_samples_num, u_shadow_settings.pcss_blocker_search_samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_shadow_dir_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, 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<f32>(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<f32>, safety_offset: bool) -> vec2<f32> {
|
||||||
|
let atlas_dimensions = textureDimensions(t_shadow_maps_atlas);
|
||||||
|
|
||||||
|
// get the rect of the frame as a vec4
|
||||||
|
var region_rect = vec4<f32>(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<f32>(
|
||||||
|
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<f32>, receiver_depth: f32, bias: f32, shadow_u: LightShadowMapUniform) -> vec2<f32> {
|
||||||
|
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<f32>(avg_dist / b, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pcss_dir_light(tex_coords: vec2<f32>, 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<f32>, 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<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, 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<LightShadowMapUniform, 6>(
|
||||||
|
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<f32>, test_depth: f32, shadow_us: array<LightShadowMapUniform, 6>, 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<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, 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<f32>(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<f32>, 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);
|
||||||
|
}
|
|
@ -1,38 +1,16 @@
|
||||||
|
#define_module lyra::shadows::depth_pass
|
||||||
|
#import lyra::shadows::structs::{LightShadowMapUniform}
|
||||||
|
|
||||||
struct TransformData {
|
struct TransformData {
|
||||||
transform: mat4x4<f32>,
|
transform: mat4x4<f32>,
|
||||||
normal_matrix: mat4x4<f32>,
|
normal_matrix: mat4x4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextureAtlasFrame {
|
|
||||||
offset: vec2<u32>,
|
|
||||||
size: vec2<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LightShadowMapUniform {
|
|
||||||
light_space_matrix: mat4x4<f32>,
|
|
||||||
atlas_frame: TextureAtlasFrame,
|
|
||||||
near_plane: f32,
|
|
||||||
far_plane: f32,
|
|
||||||
light_size_uv: f32,
|
|
||||||
light_pos: vec3<f32>,
|
|
||||||
/// 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)
|
@group(0) @binding(0)
|
||||||
var<storage, read> u_light_shadow: array<LightShadowMapUniform>;
|
var<storage, read> u_light_shadow: array<LightShadowMapUniform>;
|
||||||
/*@group(0) @binding(1)
|
|
||||||
var<uniform> u_light_pos: vec3<f32>;
|
|
||||||
@group(0) @binding(2)
|
|
||||||
var<uniform> u_light_far_plane: f32;*/
|
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> u_model_transform_data: TransformData;
|
var<uniform> u_model_transform_data: TransformData;
|
||||||
|
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position)
|
@builtin(position)
|
||||||
clip_position: vec4<f32>,
|
clip_position: vec4<f32>,
|
|
@ -0,0 +1,29 @@
|
||||||
|
#define_module lyra::shadows::structs
|
||||||
|
|
||||||
|
struct TextureAtlasFrame {
|
||||||
|
/*offset: vec2<u32>,
|
||||||
|
size: vec2<u32>,*/
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LightShadowMapUniform {
|
||||||
|
light_space_matrix: mat4x4<f32>,
|
||||||
|
atlas_frame: TextureAtlasFrame,
|
||||||
|
near_plane: f32,
|
||||||
|
far_plane: f32,
|
||||||
|
light_size_uv: f32,
|
||||||
|
light_pos: vec3<f32>,
|
||||||
|
/// 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,
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 70daf320827f64b325a77718df07177d74d7ea58
|
Loading…
Reference in New Issue