From b45c2f4fab832d1592ebfa8f716cc561f85e4016 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 13 Jul 2024 00:56:09 -0400 Subject: [PATCH] render: point light shadows in texture atlas, fix bug with unaligned GpuSlotBuffer --- examples/shadows/src/main.rs | 16 +- lyra-game/src/render/graph/passes/shadows.rs | 127 ++++++++++------ lyra-game/src/render/shaders/base.wgsl | 146 ++++++++++++++++--- lyra-game/src/render/shaders/shadows.wgsl | 39 ++++- lyra-game/src/render/slot_buffer.rs | 10 +- 5 files changed, 258 insertions(+), 80 deletions(-) diff --git a/examples/shadows/src/main.rs b/examples/shadows/src/main.rs index 70650a8..49aa82a 100644 --- a/examples/shadows/src/main.rs +++ b/examples/shadows/src/main.rs @@ -161,13 +161,13 @@ fn setup_scene_plugin(game: &mut Game) { DirectionalLight { enabled: true, color: Vec3::new(1.0, 0.95, 0.9), - intensity: 1.0, + intensity: 0.5, }, light_tran, )); world.spawn(( - cube_mesh.clone(), + //cube_mesh.clone(), PointLight { enabled: true, color: Vec3::new(0.133, 0.098, 0.91), @@ -177,6 +177,18 @@ fn setup_scene_plugin(game: &mut Game) { }, Transform::from_xyz(5.0, -2.5, -3.3), )); + + 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(); diff --git a/lyra-game/src/render/graph/passes/shadows.rs b/lyra-game/src/render/graph/passes/shadows.rs index fdd6a4c..f11b8c1 100644 --- a/lyra-game/src/render/graph/passes/shadows.rs +++ b/lyra-game/src/render/graph/passes/shadows.rs @@ -1,7 +1,6 @@ use std::{ collections::VecDeque, mem, - num::NonZeroU64, rc::Rc, sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; @@ -13,13 +12,13 @@ use lyra_ecs::{ use lyra_game_derive::RenderGraphLabel; use lyra_math::{Angle, Transform}; use rustc_hash::FxHashMap; -use tracing::{debug, warn}; +use tracing::warn; use wgpu::util::DeviceExt; use crate::render::{ graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue}, light::{directional::DirectionalLight, LightType, PointLight}, - resource::{RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, + resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, transform_buffer_storage::TransformBuffers, vertex::Vertex, AtlasFrame, GpuSlotBuffer, TextureAtlas, @@ -68,6 +67,7 @@ pub struct ShadowMapsPass { render_meshes: Option, mesh_buffers: Option, pipeline: Option, + point_light_pipeline: Option, atlas: LightShadowMapAtlas, /// The depth map atlas sampler @@ -84,10 +84,8 @@ impl ShadowMapsPass { visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Storage { read_only: true }, - has_dynamic_offset: true, - min_binding_size: Some( - NonZeroU64::new(mem::size_of::() as _).unwrap(), - ), + has_dynamic_offset: false, + min_binding_size: None, }, count: None, }], @@ -98,7 +96,7 @@ impl ShadowMapsPass { device, wgpu::TextureFormat::Depth32Float, wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, - SHADOW_SIZE * 4, + SHADOW_SIZE * 8, ); let atlas_size_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -121,12 +119,11 @@ impl ShadowMapsPass { let cap = device.limits().max_storage_buffer_binding_size as u64 / mem::size_of::() as u64; - let uniforms_buffer = GpuSlotBuffer::new_aligned( + let uniforms_buffer = GpuSlotBuffer::new( device, Some("buffer_shadow_maps_light"), wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, cap, - 256, ); let uniforms_bg = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -137,7 +134,7 @@ impl ShadowMapsPass { resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: uniforms_buffer.buffer(), offset: 0, - size: Some(NonZeroU64::new(mem::size_of::() as _).unwrap()), + size: None, }), }], }); @@ -152,6 +149,7 @@ impl ShadowMapsPass { render_meshes: None, mesh_buffers: None, pipeline: None, + point_light_pipeline: None, atlas_sampler: Rc::new(sampler), atlas: LightShadowMapAtlas(Arc::new(RwLock::new(atlas))), @@ -165,6 +163,7 @@ impl ShadowMapsPass { light_type: LightType, entity: Entity, light_pos: Transform, + far_plane: f32, ) -> LightDepthMap { const NEAR_PLANE: f32 = 0.1; const FAR_PLANE: f32 = 45.0; @@ -193,6 +192,11 @@ impl ShadowMapsPass { let u = LightShadowUniform { space_mat: light_proj, atlas_frame, + near_plane: NEAR_PLANE, + far_plane, + _padding1: [0; 2], + light_pos: light_pos.translation, + _padding2: 0, }; let uniform_index = self.light_uniforms_buffer.insert(queue, &u); @@ -207,10 +211,11 @@ impl ShadowMapsPass { Angle::Degrees(90.0).to_radians(), aspect, NEAR_PLANE, - FAR_PLANE, + far_plane, ); let light_trans = light_pos.translation; + // right, left, top, bottom, near, and far let views = [ projection * glam::Mat4::look_at_rh( @@ -287,6 +292,11 @@ impl ShadowMapsPass { &LightShadowUniform { space_mat: views[i], atlas_frame: frames[i], + near_plane: NEAR_PLANE, + far_plane, + _padding1: [0; 2], + light_pos: light_trans, + _padding2: 0, }, ); indices[i] = uniform_i; @@ -380,19 +390,18 @@ impl Node for ShadowMapsPass { /* for (entity, pos, (has_dir, has_point)) in world.view_iter::<(Entities, &Transform, Or, Has>)>() { if !self.depth_maps.contains_key(&entity) { - let light_type = if has_dir.is_some() { - LightType::Directional + // TODO: calculate far plane + let (light_type, far_plane) = if has_dir.is_some() { + (LightType::Directional, 45.0) } else if has_point.is_some() { - LightType::Point + (LightType::Point, 45.0) } else { todo!("Spot lights") }; - debug!("Creating depth map for {light_type:?}"); - // TODO: dont pack the textures as they're added let atlas_index = - self.create_depth_map(&context.queue, light_type, entity, *pos); + self.create_depth_map(&context.queue, light_type, entity, *pos, far_plane); index_components_queue.push_back((entity, atlas_index)); } } */ @@ -401,7 +410,7 @@ impl Node for ShadowMapsPass { if !self.depth_maps.contains_key(&entity) { // TODO: dont pack the textures as they're added let atlas_index = - self.create_depth_map(&context.queue, LightType::Directional, entity, *pos); + self.create_depth_map(&context.queue, LightType::Directional, entity, *pos, 45.0); index_components_queue.push_back((entity, atlas_index)); } } @@ -410,7 +419,7 @@ impl Node for ShadowMapsPass { if !self.depth_maps.contains_key(&entity) { // TODO: dont pack the textures as they're added let atlas_index = - self.create_depth_map(&context.queue, LightType::Point, entity, *pos); + self.create_depth_map(&context.queue, LightType::Point, entity, *pos, 30.0); index_components_queue.push_back((entity, atlas_index)); } } @@ -439,18 +448,14 @@ impl Node for ShadowMapsPass { &graph.device, &RenderPipelineDescriptor { label: Some("pipeline_shadows".into()), - layouts: vec![bgl, transforms], + layouts: vec![bgl.clone(), transforms.clone()], push_constant_ranges: vec![], vertex: VertexState { module: shader.clone(), entry_point: "vs_main".into(), buffers: vec![Vertex::position_desc().into()], }, - fragment: None, /* Some(FragmentState { - module: shader, - entry_point: "fs_main".into(), - targets: vec![], - }), */ + fragment: None, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, @@ -459,6 +464,40 @@ impl Node for ShadowMapsPass { bias: wgpu::DepthBiasState::default(), }), primitive: wgpu::PrimitiveState { + //cull_mode: Some(wgpu::Face::Front), + cull_mode: Some(wgpu::Face::Back), + ..Default::default() + }, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }, + )); + + self.point_light_pipeline = Some(RenderPipeline::create( + &graph.device, + &RenderPipelineDescriptor { + label: Some("pipeline_point_light_shadows".into()), + layouts: vec![bgl, transforms], + push_constant_ranges: vec![], + vertex: VertexState { + module: shader.clone(), + entry_point: "vs_main".into(), + buffers: vec![Vertex::position_desc().into()], + }, + fragment: Some(FragmentState { + module: shader, + entry_point: "fs_point_light_main".into(), + targets: vec![], + }), + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + primitive: wgpu::PrimitiveState { + //cull_mode: Some(wgpu::Face::Front), cull_mode: Some(wgpu::Face::Back), ..Default::default() }, @@ -477,6 +516,7 @@ impl Node for ShadowMapsPass { ) { let encoder = context.encoder.as_mut().unwrap(); let pipeline = self.pipeline.as_ref().unwrap(); + let point_light_pipeline = self.point_light_pipeline.as_ref().unwrap(); let render_meshes = self.render_meshes(); let mesh_buffers = self.mesh_buffers(); @@ -495,17 +535,15 @@ impl Node for ShadowMapsPass { stencil_ops: None, }), }); - pass.set_pipeline(&pipeline); for light_depth_map in self.depth_maps.values() { match light_depth_map.light_type { LightType::Directional => { - let frame = atlas.texture_frame(light_depth_map.atlas_index) - .expect("missing atlas frame of light"); - let u_offset = self.light_uniforms_buffer.offset_of(light_depth_map.uniform_index[0]) as u32; + pass.set_pipeline(&pipeline); - //debug!("Rendering directional light with atlas {} uniform index {} and offset {}, in viewport {:?}", light_depth_map.atlas_index, light_depth_map.uniform_index[0], u_offset, frame); + let frame = atlas.texture_frame(light_depth_map.atlas_index) + .expect("missing atlas frame for light"); light_shadow_pass_impl( &mut pass, @@ -514,17 +552,16 @@ impl Node for ShadowMapsPass { &mesh_buffers, &transforms, &frame, - u_offset, + light_depth_map.uniform_index[0] as _, ); }, LightType::Point => { + pass.set_pipeline(&point_light_pipeline); + for side in 0..6 { let frame = atlas.texture_frame(light_depth_map.atlas_index + side) .expect("missing atlas frame of light"); let ui = light_depth_map.uniform_index[side as usize]; - let u_offset = self.light_uniforms_buffer.offset_of(ui) as u32; - - //debug!("Rendering point light side {side} with atlas {} uniform index {ui} and offset {u_offset} and viewport {:?}", light_depth_map.atlas_index + side, frame); light_shadow_pass_impl( &mut pass, @@ -533,7 +570,7 @@ impl Node for ShadowMapsPass { &mesh_buffers, &transforms, &frame, - u_offset, + ui as _, ); } }, @@ -550,7 +587,7 @@ fn light_shadow_pass_impl<'a>( mesh_buffers: &'a RenderAssets, transforms: &'a TransformBuffers, shadow_atlas_viewport: &AtlasFrame, - uniform_offset: u32, + uniform_index: u32, ) { // only render to the light's map in the atlas pass.set_viewport( @@ -579,7 +616,7 @@ fn light_shadow_pass_impl<'a>( let buffers = buffers.unwrap(); //let uniform_index = light_uniforms_buffer.offset_of(light_depth_map.uniform_index[0]) as u32; - pass.set_bind_group(0, &uniforms_bind_group, &[uniform_offset]); + pass.set_bind_group(0, &uniforms_bind_group, &[]); // Get the bindgroup for job's transform and bind to it using an offset. let bindgroup = transforms.bind_group(job.transform_id); @@ -595,7 +632,7 @@ fn light_shadow_pass_impl<'a>( buffers.buffer_vertex.buffer().slice(..), ); pass.set_index_buffer(indices.buffer().slice(..), *idx_type); - pass.draw_indexed(0..indices_len, 0, 0..1); + pass.draw_indexed(0..indices_len, 0, uniform_index..uniform_index + 1); } else { let vertex_count = buffers.buffer_vertex.count(); @@ -603,7 +640,7 @@ fn light_shadow_pass_impl<'a>( buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..), ); - pass.draw(0..vertex_count as u32, 0..1); + pass.draw(0..vertex_count as u32, uniform_index..uniform_index + 1); } } } @@ -613,6 +650,11 @@ fn light_shadow_pass_impl<'a>( pub struct LightShadowUniform { space_mat: glam::Mat4, atlas_frame: AtlasFrame, // 2xUVec2 (4xf32), so no padding needed + near_plane: f32, + far_plane: f32, + _padding1: [u32; 2], + light_pos: glam::Vec3, + _padding2: u32, } /// A component that stores the ID of a shadow map in the shadow map atlas for the entities. @@ -648,8 +690,3 @@ impl LightShadowMapAtlas { self.0.write().unwrap() } } - -/* fn uniform_index_offset(limits: &wgpu::Limits, uniform_idx: u64) -> u32 { - let t = uniform_idx as u32 % (limits.max_storage_buffer_binding_size / mem::size_of::() as u32); - t * limits.min_uniform_buffer_offset_alignment -} */ diff --git a/lyra-game/src/render/shaders/base.wgsl b/lyra-game/src/render/shaders/base.wgsl index 9e441ec..d6f15db 100755 --- a/lyra-game/src/render/shaders/base.wgsl +++ b/lyra-game/src/render/shaders/base.wgsl @@ -23,8 +23,12 @@ struct VertexOutput { } struct TextureAtlasFrame { - offset: vec2, - size: vec2, + /*offset: vec2, + size: vec2,*/ + x: u32, + y: u32, + width: u32, + height: u32, } struct TransformData { @@ -111,11 +115,9 @@ var s_diffuse: sampler; struct LightShadowMapUniform { light_space_matrix: mat4x4, atlas_frame: TextureAtlasFrame, -} - -struct LightShadowMapUniformAligned { - @align(256) - inner: LightShadowMapUniform + near_plane: f32, + far_plane: f32, + light_pos: vec3, } @group(4) @binding(0) @@ -130,7 +132,7 @@ var s_shadow_maps_atlas: sampler; @group(5) @binding(2) var u_shadow_maps_atlas_size: vec2; @group(5) @binding(3) -var u_light_shadow: array; +var u_light_shadow: array; @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { @@ -152,21 +154,22 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let light_offset = tile.x; let light_count = tile.y; - let atlas_dimensions: vec2 = textureDimensions(t_shadow_maps_atlas); + 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]; + let light_dir = normalize(-light.direction); if (light.light_ty == LIGHT_TY_DIRECTIONAL) { - let light_dir = normalize(-light.direction); - let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]].inner; + let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]]; let frag_pos_light_space = shadow_u.light_space_matrix * vec4(in.world_position, 1.0); let shadow = calc_shadow_dir_light(in.world_normal, light_dir, frag_pos_light_space, atlas_dimensions, shadow_u.atlas_frame); 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) { - light_res += blinn_phong_point_light(in.world_position, in.world_normal, light, u_material, specular_color); + let shadow = calc_shadow_point(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); } else if (light.light_ty == LIGHT_TY_SPOT) { light_res += blinn_phong_spot_light(in.world_position, in.world_normal, light, u_material, specular_color); } @@ -176,9 +179,70 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { return vec4(light_object_res, object_color.a); } +/// Get the cube map side index of a 3d texture coord +/// +/// 0 -> UNKNOWN +/// 1 -> right +/// 2 -> left +/// 3 -> top +/// 4 -> bottom +/// 5 -> near +/// 6 -> far +fn get_side_idx(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 = normalize(res); + //res.y = 1.0-res.y; // invert y because wgsl + //let t = res.x; + //res.x = res.y; + + //res.y = 1.0 - t; + res.y = 1.0 - res.y; + //res.x = 1.0 - res.x; + + return vec3(res, f32(cube_idx)); +} + fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light_space: vec4, atlas_dimensions: vec2, atlas_region: TextureAtlasFrame) -> f32 { var proj_coords = frag_pos_light_space.xyz / frag_pos_light_space.w; - // for some reason the y component is clipped after transforming + // for some reason the y component is flipped after transforming proj_coords.y = -proj_coords.y; // dont cast shadows outside the light's far plane @@ -190,8 +254,8 @@ fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light let xy_remapped = proj_coords.xy * 0.5 + 0.5; // no need to get the y since the maps are square - let atlas_start = f32(atlas_region.offset.x) / f32(atlas_dimensions.x); - let atlas_end = f32(atlas_region.offset.x + atlas_region.size.x) / f32(atlas_dimensions.x); + let atlas_start = f32(atlas_region.x) / f32(atlas_dimensions.x); + let atlas_end = f32(atlas_region.x + atlas_region.width) / f32(atlas_dimensions.x); // lerp the tex coords to the shadow map for this light. proj_coords.x = mix(atlas_start, atlas_end, xy_remapped.x); proj_coords.y = mix(atlas_start, atlas_end, xy_remapped.y); @@ -204,7 +268,7 @@ fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light // must manually apply offset to the texture coords since `textureSampleLevel` requires a // const value. - let offset_coords = proj_coords.xy + (vec2(atlas_region.offset) / vec2(atlas_dimensions)); + let offset_coords = proj_coords.xy + (vec2(f32(atlas_region.x), f32(atlas_region.y)) / vec2(atlas_dimensions)); let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, offset_coords, 0.0); let current_depth = proj_coords.z; @@ -218,8 +282,50 @@ fn calc_shadow_dir_light(normal: vec3, light_dir: vec3, frag_pos_light return shadow; } -fn calc_shadow_point(world_pos: vec3, atlas_dimensions: vec2, atlas_regions: array) -> f32 { - return 0.0; +fn calc_shadow_point(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 = get_side_idx(normalize(frag_to_light)); + var coords_2d = temp.xy; + let cube_idx = i32(temp.z); + + /// if an unknown cube side was returned, something is broken + if cube_idx == 0 { + return 0.0; + } + + var indices = light.light_shadow_uniform_index; + let i = indices[cube_idx - 1]; + let u: LightShadowMapUniform = u_light_shadow[i]; + + // get the atlas frame in [0; 1] in the atlas texture + // z is width, w is height + var region_coords = vec4(f32(u.atlas_frame.x), f32(u.atlas_frame.y), f32(u.atlas_frame.width), f32(u.atlas_frame.height)); + region_coords /= f32(atlas_dimensions.x); + + // simulate `ClampToBorder`, not creating shadows past the shadow map regions + if (coords_2d.x >= 1.0 || coords_2d.y >= 1.0) { + return 0.0; + } + + // get the coords inside of the region + coords_2d.x = mix(region_coords.x, region_coords.x + region_coords.z, coords_2d.x); + coords_2d.y = mix(region_coords.y, region_coords.y + region_coords.w, coords_2d.y); + + var closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, coords_2d, 0.0); + let current_depth = length(frag_to_light); + + // convert depth from [0; 1] to the original depth value + closest_depth *= u.far_plane; + + // use a bias to avoid shadow acne + let bias = max(0.05 * (1.0 - dot(world_normal, light_dir)), 0.005); + + var shadow = 0.0; + if current_depth - bias > closest_depth { + shadow = 1.0; + } + + return shadow; } fn debug_grid(in: VertexOutput) -> vec4 { @@ -266,7 +372,7 @@ fn blinn_phong_dir_light(world_pos: vec3, world_norm: vec3, dir_light: return (ambient_color + (1.0 - shadow) * (diffuse_color + specular_color)) * dir_light.intensity; } -fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_light: Light, material: Material, specular_factor: vec3) -> vec3 { +fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_light: Light, material: Material, specular_factor: vec3, shadow: f32) -> vec3 { let light_color = point_light.color.xyz; let light_pos = point_light.position.xyz; let camera_view_pos = u_camera.position; @@ -296,7 +402,7 @@ fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_li diffuse_color *= attenuation; specular_color *= attenuation; - return (ambient_color + diffuse_color + specular_color) * point_light.intensity; + return (ambient_color + (1.0 - shadow) * (diffuse_color + specular_color)) * point_light.intensity; } fn blinn_phong_spot_light(world_pos: vec3, world_norm: vec3, spot_light: Light, material: Material, specular_factor: vec3) -> vec3 { diff --git a/lyra-game/src/render/shaders/shadows.wgsl b/lyra-game/src/render/shaders/shadows.wgsl index 8ea73d3..b8e17fc 100644 --- a/lyra-game/src/render/shaders/shadows.wgsl +++ b/lyra-game/src/render/shaders/shadows.wgsl @@ -11,23 +11,54 @@ struct TextureAtlasFrame { struct LightShadowMapUniform { light_space_matrix: mat4x4, atlas_frame: TextureAtlasFrame, + near_plane: f32, + far_plane: f32, + light_pos: vec3, } @group(0) @binding(0) -var u_light_shadow: LightShadowMapUniform; +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, + @location(0) world_pos: vec3, + @location(1) instance_index: u32, } @vertex fn vs_main( - @location(0) position: vec3 + @location(0) position: vec3, + @builtin(instance_index) instance_index: u32, ) -> VertexOutput { - let pos = u_light_shadow.light_space_matrix * u_model_transform_data.transform * vec4(position, 1.0); - return VertexOutput(pos); + let world_pos = u_model_transform_data.transform * vec4(position, 1.0); + let pos = u_light_shadow[instance_index].light_space_matrix * world_pos; + return VertexOutput(pos, world_pos.xyz, instance_index); +} + +struct FragmentOutput { + @builtin(frag_depth) depth: f32, +} + +/// Fragment shader used for point lights (or other perspective lights) to create linear depth +@fragment +fn fs_point_light_main( + in: VertexOutput +) -> FragmentOutput { + let u = u_light_shadow[in.instance_index]; + + var light_dis = length(in.world_pos - u.light_pos); + + // map to [0; 1] range by dividing by far plane + light_dis = light_dis / u.far_plane; + + return FragmentOutput(light_dis); } \ No newline at end of file diff --git a/lyra-game/src/render/slot_buffer.rs b/lyra-game/src/render/slot_buffer.rs index be830ce..67e5a82 100644 --- a/lyra-game/src/render/slot_buffer.rs +++ b/lyra-game/src/render/slot_buffer.rs @@ -54,19 +54,11 @@ impl GpuSlotBuffer { /// Calculates the byte offset in the buffer of the element at `i`. pub fn offset_of(&self, i: u64) -> u64 { - /* let offset = i * mem::size_of::() as u64; - - if let Some(align) = self.alignment { - round_mult::up(offset, NonZeroU64::new(align).unwrap()).unwrap() - } else { - offset - } */ - if let Some(align) = self.alignment { let transform_index = i % self.capacity; transform_index * align } else { - mem::size_of::() as u64 + i * mem::size_of::() as u64 } }