Compare commits

..

No commits in common. "8545e7e27d1b33c2963d6cd9854f74893fda197e" and "8c1738334c2ee46317fa279ea9dd8acb61a8c366" have entirely different histories.

5 changed files with 95 additions and 128 deletions

View File

@ -5,7 +5,7 @@ use lyra_engine::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
}, },
math::{self, Quat, Transform, Vec3}, math::{self, Angle, Quat, Transform, Vec3},
render::{ render::{
graph::{ShadowCasterSettings, ShadowFilteringMode}, graph::{ShadowCasterSettings, ShadowFilteringMode},
light::{directional::DirectionalLight, PointLight, SpotLight}, light::{directional::DirectionalLight, PointLight, SpotLight},
@ -184,9 +184,6 @@ fn setup_scene_plugin(game: &mut Game) {
}, },
ShadowCasterSettings { ShadowCasterSettings {
filtering_mode: ShadowFilteringMode::Pcss, filtering_mode: ShadowFilteringMode::Pcss,
pcf_samples_num: 64,
pcss_blocker_search_samples: 36,
constant_depth_bias_scale: 5.0,
..Default::default() ..Default::default()
}, },
light_tran, light_tran,
@ -220,7 +217,7 @@ fn setup_scene_plugin(game: &mut Game) {
Quat::from_euler(math::EulerRot::XYZ, 0.0, math::Angle::Degrees(-45.0).to_radians(), 0.0), Quat::from_euler(math::EulerRot::XYZ, 0.0, math::Angle::Degrees(-45.0).to_radians(), 0.0),
Vec3::new(0.15, 0.15, 0.15), Vec3::new(0.15, 0.15, 0.15),
); );
world.spawn(( world.spawn((
SpotLight { SpotLight {
enabled: true, enabled: true,
@ -238,12 +235,29 @@ fn setup_scene_plugin(game: &mut Game) {
t, t,
//cube_mesh.clone(), //cube_mesh.clone(),
)); ));
/* world.spawn((
//cube_mesh.clone(),
PointLight {
enabled: true,
color: Vec3::new(0.278, 0.984, 0.0),
intensity: 2.0,
range: 9.0,
..Default::default()
},
Transform::from_xyz(-0.5, 2.0, -5.0),
)); */
} }
let mut camera = CameraComponent::new_3d(); let mut camera = CameraComponent::new_3d();
//camera.transform.translation += math::Vec3::new(0.0, 2.0, 10.5);
camera.transform.translation = math::Vec3::new(-1.0, -10.0, -1.5); camera.transform.translation = math::Vec3::new(-1.0, -10.0, -1.5);
camera.transform.rotate_x(math::Angle::Degrees(-27.0)); camera.transform.rotate_x(math::Angle::Degrees(-27.0));
camera.transform.rotate_y(math::Angle::Degrees(-90.0)); camera.transform.rotate_y(math::Angle::Degrees(-90.0));
/* camera.transform.translation = math::Vec3::new(15.0, -8.0, 1.0);
camera.transform.rotate_x(math::Angle::Degrees(-27.0));
camera.transform.rotate_y(math::Angle::Degrees(90.0)); */
world.spawn((camera, FreeFlyCamera::default())); world.spawn((camera, FreeFlyCamera::default()));
} }

View File

@ -201,17 +201,6 @@ impl ShadowMapsPass {
let has_shadow_settings = if are_settings_custom { let has_shadow_settings = if are_settings_custom {
1 1
} else { 0 }; } else { 0 };
/* let (has_shadow_settings, pcf_samples_num, pcss_samples_num) = if are_settings_custom {
(1, u.pcf_samples_num, u.pcss_blocker_search_samples)
} else {
(0, , 0)
}; */
/* shadow_settings.map(|ss| {
let u = ShadowSettingsUniform::new(ss.filtering_mode, ss.pcf_samples_num, ss.pcss_blocker_search_samples);
(1, u.pcf_samples_num, u.pcss_blocker_search_samples)
}).unwrap_or((0, 0, 0)); */
let (start_atlas_idx, uniform_indices) = match light_type { let (start_atlas_idx, uniform_indices) = match light_type {
LightType::Directional => { LightType::Directional => {
@ -267,36 +256,27 @@ impl ShadowMapsPass {
(atlas_index, indices) (atlas_index, indices)
} }
LightType::Spotlight => { LightType::Spotlight => {
let directional_size = SHADOW_SIZE * 4; // allocate a single frame in the shadow map atlas
// directional lights require a single map, so allocate that in the atlas.
let atlas_index = atlas let atlas_index = atlas
.pack(directional_size.x as _, directional_size.y as _) .pack(SHADOW_SIZE.x as _, SHADOW_SIZE.y as _)
.expect("failed to pack new shadow map into texture atlas"); .expect("failed to pack new shadow map into texture atlas");
let atlas_frame = atlas.texture_frame(atlas_index).expect("Frame missing"); let atlas_frame = atlas.texture_frame(atlas_index).expect("Frame missing");
let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32; let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32;
let projection = glam::Mat4::perspective_rh( let projection = glam::Mat4::perspective_rh(
//Angle::Degrees(90.0).to_radians(),
(light_half_outer_angle.unwrap() * 2.0).to_radians(), (light_half_outer_angle.unwrap() * 2.0).to_radians(),
aspect, aspect,
shadow_settings.near_plane, shadow_settings.near_plane,
shadow_settings.far_plane, shadow_settings.far_plane,
); );
// honestly no clue why this works, but I got it from here and the results are good
// https://github.com/asylum2010/Asylum_Tutorials/blob/423e5edfaee7b5ea450a450e65f2eabf641b2482/ShaderTutors/43_ShadowMapFiltering/main.cpp#L323
/* let frustum_size = Vec2::new(0.5 * projection.col(0).x, 0.5 * projection.col(1).y);
// maybe its better to make this a vec2 on the gpu?
let size_avg = (frustum_size.x + frustum_size.y) / 2.0;
let light_size_uv = 0.2 * size_avg; */
let light_trans = light_pos.translation; let light_trans = light_pos.translation;
let look_view = glam::Mat4::look_at_rh( let forward = light_pos.forward();
light_trans, let up = light_pos.up();
light_trans + glam::vec3(1.0, 0.0, 0.0), let view = glam::Mat4::look_to_rh(light_trans, forward, up);
glam::vec3(0.0, -1.0, 0.0),
);
let light_proj = projection * look_view; let light_proj = projection * view;
let u = LightShadowUniform { let u = LightShadowUniform {
space_mat: light_proj, space_mat: light_proj,
@ -686,30 +666,6 @@ impl Node for ShadowMapsPass {
} }
} }
for (entity, pos, shadow_settings, _) in world.view_iter::<(
Entities,
&Transform,
Option<&ShadowCasterSettings>,
Has<PointLight>,
)>() {
if !self.depth_maps.contains_key(&entity) {
let (custom_settings, shadow_settings) = shadow_settings
.map(|ss| (true, ss.clone()))
.unwrap_or((false, settings));
let atlas_index = self.create_depth_map(
&context.queue,
LightType::Point,
entity,
*pos,
None,
custom_settings,
shadow_settings,
);
index_components_queue.push_back((entity, atlas_index));
}
}
for (entity, pos, shadow_settings, spot) in world.view_iter::<( for (entity, pos, shadow_settings, spot) in world.view_iter::<(
Entities, Entities,
&Transform, &Transform,
@ -734,6 +690,30 @@ impl Node for ShadowMapsPass {
} }
} }
for (entity, pos, shadow_settings, _) in world.view_iter::<(
Entities,
&Transform,
Option<&ShadowCasterSettings>,
Has<PointLight>,
)>() {
if !self.depth_maps.contains_key(&entity) {
let (custom_settings, shadow_settings) = shadow_settings
.map(|ss| (true, ss.clone()))
.unwrap_or((false, settings));
let atlas_index = self.create_depth_map(
&context.queue,
LightType::Point,
entity,
*pos,
None,
custom_settings,
shadow_settings,
);
index_components_queue.push_back((entity, atlas_index));
}
}
// now consume from the queue adding the components to the entities // now consume from the queue adding the components to the entities
while let Some((entity, depth)) = index_components_queue.pop_front() { while let Some((entity, depth)) = index_components_queue.pop_front() {
world.insert( world.insert(
@ -864,7 +844,7 @@ impl Node for ShadowMapsPass {
&frame, &frame,
light_depth_map.uniform_index[0] as _, light_depth_map.uniform_index[0] as _,
); );
} },
LightType::Point => { LightType::Point => {
pass.set_pipeline(&point_light_pipeline); pass.set_pipeline(&point_light_pipeline);
@ -884,9 +864,10 @@ impl Node for ShadowMapsPass {
ui as _, ui as _,
); );
} }
} },
LightType::Spotlight => { LightType::Spotlight => {
pass.set_pipeline(&pipeline); pass.set_pipeline(&pipeline);
//pass.set_pipeline(&point_light_pipeline);
let frame = atlas let frame = atlas
.texture_frame(light_depth_map.atlas_index) .texture_frame(light_depth_map.atlas_index)
@ -901,7 +882,7 @@ impl Node for ShadowMapsPass {
&frame, &frame,
light_depth_map.uniform_index[0] as _, light_depth_map.uniform_index[0] as _,
); );
}, }
} }
} }
} }
@ -1070,35 +1051,17 @@ pub enum ShadowFilteringMode {
None, None,
/// Uses hardware features for 2x2 PCF. /// Uses hardware features for 2x2 PCF.
Pcf2x2, Pcf2x2,
Pcf,
#[default] #[default]
Pcf,
/// Percentage-Closer Soft Shadows
/// https://developer.download.nvidia.com/shaderlibrary/docs/shadow_PCSS.pdf
///
/// PCSS is only implemented for directional lights. Use PCF for point and spot lights instead.
/// PCSS is expensive per-frame, so it has not been implemented for them. If you use this for
/// point and/or spot lights, the renderer will fall back to PCF.
Pcss, Pcss,
} }
/* #[derive(Debug, Copy, Clone)]
pub struct ShadowSettings {
pub filtering_mode: ShadowFilteringMode,
/// How many PCF filtering samples are used per dimension.
///
/// A value of 25 is common, this is maxed to 128.
pub pcf_samples_num: u32,
/// How many samples are used for the PCSS blocker search step.
///
/// Multiple samples are required to avoid holes int he penumbra due to missing blockers.
/// A value of 25 is common, this is maxed to 128.
pub pcss_blocker_search_samples: u32,
}
impl Default for ShadowSettings {
fn default() -> Self {
Self {
filtering_mode: ShadowFilteringMode::default(),
pcf_samples_num: 25,
pcss_blocker_search_samples: 25,
}
}
} */
const PCF_SAMPLES_NUM_MAX: u32 = 128; const PCF_SAMPLES_NUM_MAX: u32 = 128;
const PCSS_SAMPLES_NUM_MAX: u32 = 128; const PCSS_SAMPLES_NUM_MAX: u32 = 128;

View File

@ -9,6 +9,8 @@ pub struct SpotLight {
pub range: f32, pub range: f32,
pub intensity: f32, pub intensity: f32,
pub smoothness: f32, pub smoothness: f32,
/// Cutoff angle that specifies the light radius.
/// This is half of the light's FOV.
pub cutoff: math::Angle, pub cutoff: math::Angle,
pub outer_cutoff: math::Angle, pub outer_cutoff: math::Angle,
} }

View File

@ -182,7 +182,6 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
if (light.light_ty == LIGHT_TY_DIRECTIONAL) { if (light.light_ty == LIGHT_TY_DIRECTIONAL) {
let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]]; let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]];
let frag_pos_light_space = shadow_u.light_space_matrix * vec4<f32>(in.world_position, 1.0);
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);
@ -190,7 +189,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
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, atlas_dimensions);
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);
} }
} }
@ -294,12 +293,12 @@ fn calc_shadow_dir_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_di
} }
// PCSS // PCSS
else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u { else if pcf_samples_num > 0u && pcss_blocker_search_samples > 0u {
shadow = pcss_dir_light(xy_remapped, current_depth, map_data); shadow = pcss_dir_light(xy_remapped, current_depth, i32(pcss_blocker_search_samples), i32(pcf_samples_num), map_data);
} }
// only PCF // only PCF
else if pcf_samples_num > 0u { else if pcf_samples_num > 0u {
let texel_size = 1.0 / f32(map_data.atlas_frame.width); let texel_size = 1.0 / f32(map_data.atlas_frame.width);
shadow = pcf_dir_light(xy_remapped, current_depth, map_data, texel_size); shadow = pcf_dir_light(xy_remapped, current_depth, map_data, i32(pcf_samples_num), texel_size);
} }
// no filtering // no filtering
else { else {
@ -353,13 +352,13 @@ fn to_atlas_frame_coords(shadow_u: LightShadowMapUniform, coords: vec2<f32>, saf
} }
/// Find the average blocker distance for a directiona llight /// 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> { fn find_blocker_distance_dir_light(tex_coords: vec2<f32>, search_samples: i32, 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); let search_width = search_width(shadow_u.near_plane, shadow_u.light_size_uv, receiver_depth);
var blockers = 0; var blockers = 0;
var avg_dist = 0.0; var avg_dist = 0.0;
let samples = i32(u_shadow_settings.pcss_blocker_search_samples); //let samples = i32(u_shadow_settings.pcss_blocker_search_samples);
for (var i = 0; i < samples; i++) { for (var i = 0; i < search_samples; i++) {
let offset_coords = tex_coords + u_pcss_poisson_disc[i] * search_width; 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 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); let z = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, new_coords, 0.0);
@ -374,8 +373,8 @@ fn find_blocker_distance_dir_light(tex_coords: vec2<f32>, receiver_depth: f32, b
return vec2<f32>(avg_dist / b, b); return vec2<f32>(avg_dist / b, b);
} }
fn pcss_dir_light(tex_coords: vec2<f32>, receiver_depth: f32, shadow_u: LightShadowMapUniform) -> f32 { fn pcss_dir_light(tex_coords: vec2<f32>, receiver_depth: f32, pcss_blocker_samples: i32, pcf_samples_num: i32, shadow_u: LightShadowMapUniform) -> f32 {
let blocker_search = find_blocker_distance_dir_light(tex_coords, receiver_depth, 0.0, shadow_u); let blocker_search = find_blocker_distance_dir_light(tex_coords, pcss_blocker_samples, receiver_depth, 0.0, shadow_u);
// If no blockers were found, exit now to save in filtering // If no blockers were found, exit now to save in filtering
if blocker_search.y == 0.0 { if blocker_search.y == 0.0 {
@ -388,13 +387,12 @@ fn pcss_dir_light(tex_coords: vec2<f32>, receiver_depth: f32, shadow_u: LightSha
// PCF // PCF
let uv_radius = penumbra_width * shadow_u.light_size_uv * shadow_u.near_plane / receiver_depth; 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); return pcf_dir_light(tex_coords, receiver_depth, shadow_u, pcf_samples_num, uv_radius);
} }
/// Calculate the shadow coefficient using PCF of a directional light /// Calculate the shadow coefficient using PCF of a directional light
fn pcf_dir_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMapUniform, uv_radius: f32) -> f32 { fn pcf_dir_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: i32, uv_radius: f32) -> f32 {
var shadow = 0.0; var shadow = 0.0;
let samples_num = i32(u_shadow_settings.pcf_samples_num);
for (var i = 0; i < samples_num; i++) { for (var i = 0; i < samples_num; i++) {
let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius; let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius;
let new_coords = to_atlas_frame_coords(shadow_u, offset, false); let new_coords = to_atlas_frame_coords(shadow_u, offset, false);
@ -441,15 +439,10 @@ fn calc_shadow_point_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_
let region_coords = to_atlas_frame_coords(u, coords_2d, true); 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); shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth);
} }
// PCSS // only PCF, PCSS is not supported so no need to check for it
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 { else if pcf_samples_num > 0u {
let texel_size = 1.0 / f32(u.atlas_frame.width); 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(frag_to_light, current_depth, uniforms, pcf_samples_num, texel_size);
//shadow = pcf_point_light(coords_2d, current_depth, u, pcf_samples_num, texel_size);
} }
// no filtering // no filtering
else { else {
@ -483,7 +476,21 @@ fn pcf_point_light(tex_coords: vec3<f32>, test_depth: f32, shadow_us: array<Ligh
return saturate(shadow); 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 { 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 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 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); let frag_pos_light_space = map_data.light_space_matrix * vec4<f32>(world_pos, 1.0);
@ -533,21 +540,6 @@ fn calc_shadow_spot_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_d
return shadow; 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));
@ -661,13 +653,14 @@ fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_ligh
let distance = length(light_pos - world_pos); let distance = length(light_pos - world_pos);
let attenuation = calc_attenuation(spot_light, distance); let attenuation = calc_attenuation(spot_light, distance);
ambient_color *= attenuation * cone; ambient_color *= attenuation * spot_light.intensity * cone;
diffuse_color *= attenuation * cone; diffuse_color *= attenuation * spot_light.intensity * cone;
specular_color *= attenuation * cone; specular_color *= attenuation * spot_light.intensity * cone;
//// end of spot light attenuation //// //// end of spot light attenuation ////
//return /*ambient_color +*/ diffuse_color + specular_color; //return /*ambient_color +*/ diffuse_color + specular_color;
return (shadow * (diffuse_color + specular_color)) * spot_light.intensity; return (shadow * (diffuse_color + specular_color));
} }
fn calc_attenuation(light: Light, distance: f32) -> f32 { fn calc_attenuation(light: Light, distance: f32) -> f32 {

View File

@ -24,15 +24,10 @@ struct LightShadowMapUniform {
@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>,