From aa79ace4017148a6ee6bf40ef19302d30f2bb458 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 10 Nov 2023 17:52:11 -0500 Subject: [PATCH] Get multiple light sources working --- examples/testbed/src/main.rs | 17 +++- src/render/light/mod.rs | 154 +++++++++++++++-------------------- src/render/renderer.rs | 5 +- src/render/shaders/base.wgsl | 13 ++- 4 files changed, 91 insertions(+), 98 deletions(-) diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 8014ee8..fb9384d 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -90,16 +90,27 @@ async fn main() { quadratic: 0.032, }; world.spawn((light,)); */ - let pos = Vec3::new(0.0, 0.0, -10.0); world.spawn(( PointLight { color: Vec3::new(1.0, 1.0, 1.0), - intensity: 2.0, + intensity: 1.0, constant: 1.0, linear: 0.045, quadratic: 0.0075, }, - TransformComponent::from(Transform::from_xyz(pos.x, pos.y, pos.z)), + TransformComponent::from(Transform::from_xyz(-2.5, 0.0, -10.0)), + ModelComponent(cube_model.clone()), + )); + + world.spawn(( + PointLight { + color: Vec3::new(0.361, 0.984, 0.0), + intensity: 1.0, + constant: 1.0, + linear: 0.045, + quadratic: 0.0075, + }, + TransformComponent::from(Transform::from_xyz(2.5, 0.0, -10.0)), ModelComponent(cube_model), )); diff --git a/src/render/light/mod.rs b/src/render/light/mod.rs index 358b667..c1c51fe 100644 --- a/src/render/light/mod.rs +++ b/src/render/light/mod.rs @@ -1,5 +1,5 @@ pub mod point; -use std::{collections::{VecDeque, HashMap}, num::NonZeroU64}; +use std::{collections::{VecDeque, HashMap}, num::NonZeroU64, marker::PhantomData}; use edict::query::EpochOf; pub use point::*; @@ -10,11 +10,10 @@ use std::mem; use crate::{math::Transform, ecs::components::TransformComponent}; -pub struct LightBuffer { - pub bind_group: wgpu::BindGroup, - /// The buffer on the gpu storing the lights - buffer: wgpu::Buffer, - device_limits: wgpu::Limits, +const MAX_LIGHT_COUNT: usize = 32; + +pub struct LightBuffer { + _phantom: PhantomData, /// The max amount of light casters that could fit in this buffer. pub max_count: usize, /// The current amount of light casters in this buffer. @@ -25,59 +24,10 @@ pub struct LightBuffer { dead_indexes: VecDeque, } -impl LightBuffer { - pub fn new(device: &wgpu::Device, layout: &wgpu::BindGroupLayout, max_count: usize) -> Self - where - U: Default + bytemuck::Pod + bytemuck::Zeroable - { - let device_limits = device.limits(); - - let buffer = device.create_buffer( - &wgpu::BufferDescriptor { - label: Some("Light buffer"), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - size: (mem::size_of::() * max_count) as u64,//render_limits.max_uniform_buffer_binding_size as u64, - mapped_at_creation: false, - /* label: Some("Light Buffer"), - contents: bytemuck::cast_slice(&[U::default()]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, */ - } - ); - - /* let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: buffer.as_entire_binding(), - } - ], - label: Some("light_bind_group"), - }); */ - - let stride = device_limits.min_uniform_buffer_offset_alignment as usize + mem::size_of::(); - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer( - wgpu::BufferBinding { - buffer: &buffer, - offset: 0, - size: Some(NonZeroU64::new(stride as u64).unwrap()) - } - ) - } - ], - label: Some("light_bind_group"), - }); - - +impl LightBuffer { + pub fn new(layout: &wgpu::BindGroupLayout, max_count: usize) -> Self { Self { - buffer, - bind_group, - device_limits, + _phantom: PhantomData::default(), max_count, current_count: 0, used_indexes: HashMap::new(), @@ -90,16 +40,15 @@ impl LightBuffer { } /// Update an existing light in the light buffer. - pub fn update_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) { + pub fn update_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) { let buffer_idx = *self.used_indexes.get(&entity) .expect("Entity for Light is not in buffer!"); - let offset = buffer_idx as u64 * self.device_limits.min_uniform_buffer_offset_alignment as u64; - queue.write_buffer(&self.buffer, offset, bytemuck::bytes_of(light)); + lights_buffer[buffer_idx] = light; } /// Add a new light to the light buffer. - pub fn add_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) { + pub fn add_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) { let buffer_idx = match self.dead_indexes.pop_front() { Some(i) => i, None => { @@ -110,15 +59,15 @@ impl LightBuffer { }; self.used_indexes.insert(entity, buffer_idx); - self.update_light(queue, entity, light); + self.update_light(lights_buffer, entity, light); } /// Update, or add a new caster, to the light buffer. - pub fn update_or_add(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) { + pub fn update_or_add(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) { if self.used_indexes.contains_key(&entity) { - self.update_light(queue, entity, light); + self.update_light(lights_buffer, entity, light); } else { - self.add_light(queue, entity, light); + self.add_light(lights_buffer, entity, light); } } @@ -132,14 +81,24 @@ impl LightBuffer { } pub struct LightUniformBuffers { + pub buffer: wgpu::Buffer, pub bindgroup_layout: wgpu::BindGroupLayout, - pub point_lights: LightBuffer, - //spotlights: LightBuffer, - //directional_light + pub bindgroup: wgpu::BindGroup, + pub lights_uniform: LightsUniform, + pub point_lights: LightBuffer, } impl LightUniformBuffers { pub fn new(device: &wgpu::Device) -> Self { + let buffer = device.create_buffer( + &wgpu::BufferDescriptor { + label: Some("Lights Uniform buffer"), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + size: mem::size_of::() as u64, + mapped_at_creation: false, + } + ); + let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ wgpu::BindGroupLayoutEntry { @@ -153,41 +112,58 @@ impl LightUniformBuffers { count: None, } ], - label: Some("light_bind_group_layout"), + label: Some("lights_bind_group_layout"), }); - let point_lights = LightBuffer::new::(device, &bindgroup_layout, 10); + let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bindgroup_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + wgpu::BufferBinding { + buffer: &buffer, + offset: 0, + size: Some(NonZeroU64::new(mem::size_of::() as u64).unwrap()) + } + ) + } + ], + label: Some("light_bind_group"), + }); + + let point_lights = LightBuffer::new(&bindgroup_layout, MAX_LIGHT_COUNT); Self { + buffer, bindgroup_layout, + bindgroup, + lights_uniform: LightsUniform::default(), point_lights, } } - pub fn update_lights(&mut self, queue: &wgpu::Queue, world: &edict::World) { - let now_epoch = world.epoch(); + pub fn update_lights(&mut self, queue: &wgpu::Queue, world_epoch: edict::epoch::EpochId, world: &edict::World) { for (entity, point_light, transform, light_epoch, transform_epoch) in world.query::<(edict::Entities, &PointLight, &TransformComponent, EpochOf, EpochOf)>().iter() { - /* if !self.point_lights.has_light(entity) || light_epoch == now_epoch || transform_epoch == now_epoch { - debug!("Updated light after update!"); + if !self.point_lights.has_light(entity) || light_epoch == world_epoch || transform_epoch == world_epoch { let uniform = PointLightUniform::from_bundle(point_light, &transform.transform); - self.point_lights.update_or_add(queue, entity, &uniform); - } */ - let uniform = PointLightUniform::from_bundle(point_light, &transform.transform); - self.point_lights.update_or_add(queue, entity, &uniform); + self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform); + } } - } - // Binds the light buffer to the render pass. - // - // Parameters: - // * `render_pass` - The render pass to bind the buffers to. - // * `point_bind_index` - The bind group index that the point light buffers will be bound to. - /* pub fn bind_lights<'a, 'b: 'a>(&'a mut self, render_pass: &'b mut wgpu::RenderPass<'b>, point_bind_index: u32) { - render_pass.set_bind_group(point_bind_index, &self.point_lights.bind_group, &[]); - //render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); - } */ + self.lights_uniform.point_light_count = self.point_lights.current_count as u32; + queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.lights_uniform])); + } +} + +#[repr(C)] +#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct LightsUniform { + point_lights: [PointLightUniform; MAX_LIGHT_COUNT], + point_light_count: u32, + _padding: [u32; 3], } #[repr(C)] diff --git a/src/render/renderer.rs b/src/render/renderer.rs index c657eaa..bc740e1 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -530,6 +530,7 @@ impl BasicRenderer { impl Renderer for BasicRenderer { fn prepare(&mut self, main_world: &mut edict::World) { let last_epoch = main_world.epoch(); + debug!("Last epoch: {last_epoch:?}"); let mut alive_entities = HashSet::new(); @@ -610,7 +611,7 @@ impl Renderer for BasicRenderer { warn!("Missing camera!"); } - self.light_buffers.update_lights(&self.queue, &main_world); + self.light_buffers.update_lights(&self.queue, last_epoch, &main_world); } fn render(&mut self) -> Result<(), wgpu::SurfaceError> { @@ -672,7 +673,7 @@ impl Renderer for BasicRenderer { // bind light //render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); - render_pass.set_bind_group(3, &self.light_buffers.point_lights.bind_group, &[]); + render_pass.set_bind_group(3, &self.light_buffers.bindgroup, &[]); ////self.light_buffers.bind_lights(&mut render_pass, 3); diff --git a/src/render/shaders/base.wgsl b/src/render/shaders/base.wgsl index ec7267c..e97c715 100755 --- a/src/render/shaders/base.wgsl +++ b/src/render/shaders/base.wgsl @@ -1,6 +1,6 @@ // Vertex shader -const max_light_count: i32 = 10; +const max_light_count: u32 = 32u; struct VertexInput { @location(0) position: vec3, @@ -29,6 +29,11 @@ struct PointLight { quadratic: f32, }; +struct Lights { + point_lights: array, + point_light_count: u32, +} + @group(1) @binding(0) var u_model_transform: mat4x4; @@ -36,7 +41,7 @@ var u_model_transform: mat4x4; var u_camera: CameraUniform; @group(3) @binding(0) -var u_point_lights: array; +var u_lights: Lights; @vertex fn vs_main( @@ -68,8 +73,8 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); var light_res = vec3(0.0); - for (var i = 0; i < 1; i++) { - light_res += blinn_phong_point_light(in.world_position, in.world_normal, u_point_lights[i]); + for (var i = 0u; i < u_lights.point_light_count; i++) { + light_res += blinn_phong_point_light(in.world_position, in.world_normal, u_lights.point_lights[i]); } let light_object_res = light_res * object_color.xyz;