From e95a45fd532318ab696571889e5f6383dc2b0527 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 10 Nov 2023 09:14:18 -0500 Subject: [PATCH 1/5] Implement a single point light with blinn-phong lighting --- Cargo.lock | 10 +++ Cargo.toml | 3 +- examples/testbed/src/free_fly_camera.rs | 2 +- src/render/camera.rs | 17 +++++ src/render/light/mod.rs | 13 ++++ src/render/light/point.rs | 6 ++ src/render/mod.rs | 3 +- src/render/renderer.rs | 91 ++++++++++++++++++++++--- src/render/shaders/base.wgsl | 51 +++++++++++++- src/render/vertex.rs | 12 +++- 10 files changed, 191 insertions(+), 17 deletions(-) mode change 100755 => 100644 Cargo.toml create mode 100644 src/render/light/mod.rs create mode 100644 src/render/light/point.rs diff --git a/Cargo.lock b/Cargo.lock index a90381f..5e4598f 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -1142,6 +1142,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1290,6 +1299,7 @@ dependencies = [ "glam", "image", "instant", + "itertools", "lyra-resource", "petgraph", "quote", diff --git a/Cargo.toml b/Cargo.toml old mode 100755 new mode 100644 index c6be1f7..73cf6fd --- a/Cargo.toml +++ b/Cargo.toml @@ -37,4 +37,5 @@ aligned-vec = "0.5.0" tracing-appender = "0.2.2" stopwatch = "0.0.7" petgraph = "0.6.4" -uuid = { version = "1.5.0", features = ["v4", "fast-rng"] } \ No newline at end of file +uuid = { version = "1.5.0", features = ["v4", "fast-rng"] } +itertools = "0.11.0" diff --git a/examples/testbed/src/free_fly_camera.rs b/examples/testbed/src/free_fly_camera.rs index fafb145..1bbbea4 100644 --- a/examples/testbed/src/free_fly_camera.rs +++ b/examples/testbed/src/free_fly_camera.rs @@ -20,7 +20,7 @@ pub struct FreeFlyCamera { impl Default for FreeFlyCamera { fn default() -> Self { Self { - speed: 3.0, + speed: 4.0, look_speed: 0.09, mouse_sensitivity: 0.4, look_with_keys: false, diff --git a/src/render/camera.rs b/src/render/camera.rs index fa72afd..b7560fd 100755 --- a/src/render/camera.rs +++ b/src/render/camera.rs @@ -35,6 +35,23 @@ impl Projection { } } +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct CameraUniform { + pub view_proj: glam::Mat4, + // vec4 is used because of the uniforms 16 byte spacing requirement + pub view_pos: glam::Vec4, +} + +impl Default for CameraUniform { + fn default() -> Self { + Self { + view_proj: glam::Mat4::IDENTITY, + view_pos: Default::default() + } + } +} + #[derive(Debug, Clone)] pub struct RenderCamera { view_proj: glam::Mat4, diff --git a/src/render/light/mod.rs b/src/render/light/mod.rs new file mode 100644 index 0000000..178c632 --- /dev/null +++ b/src/render/light/mod.rs @@ -0,0 +1,13 @@ +pub mod point; +pub use point::*; + +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct LightUniform { + pub position: glam::Vec3, + // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here + pub(crate) _padding: u32, + pub color: glam::Vec3, + // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here + pub(crate) _padding2: u32, +} \ No newline at end of file diff --git a/src/render/light/point.rs b/src/render/light/point.rs new file mode 100644 index 0000000..e7b99a4 --- /dev/null +++ b/src/render/light/point.rs @@ -0,0 +1,6 @@ +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct PointLight { + position: glam::Vec3, + color: glam::Vec3, +} \ No newline at end of file diff --git a/src/render/mod.rs b/src/render/mod.rs index e6fe716..0830bba 100755 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -10,4 +10,5 @@ pub mod shader_loader; pub mod material; pub mod camera; pub mod window; -pub mod transform_buffer_storage; \ No newline at end of file +pub mod transform_buffer_storage; +pub mod light; \ No newline at end of file diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 4aea4f9..1afffd0 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -8,6 +8,7 @@ use edict::query::EpochOf; use edict::{EntityId, Entities}; use glam::Vec3; use instant::Instant; +use itertools::izip; use tracing::{debug, warn}; use wgpu::{BindGroup, BindGroupLayout, Limits}; use wgpu::util::DeviceExt; @@ -16,10 +17,11 @@ use winit::window::Window; use crate::ecs::components::camera::CameraComponent; use crate::ecs::components::model::ModelComponent; use crate::ecs::components::transform::TransformComponent; -use crate::math::Transform; +use crate::math::{Transform, self}; -use super::camera::RenderCamera; +use super::camera::{RenderCamera, CameraUniform}; use super::desc_buf_lay::DescVertexBufferLayout; +use super::light::LightUniform; use super::texture::RenderTexture; use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers}; use super::vertex::Vertex; @@ -87,6 +89,11 @@ pub struct BasicRenderer { texture_bind_group_layout: BindGroupLayout, default_texture_bind_group: BindGroup, depth_buffer_texture: RenderTexture, + + point_light_uniform: LightUniform, + point_light_buffer: wgpu::Buffer, + point_light_bind_group_layout: BindGroupLayout, + point_light_bind_group: BindGroup, } impl BasicRenderer { @@ -236,10 +243,51 @@ impl BasicRenderer { next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 }, }; + let point_light_uniform = LightUniform { + position: glam::Vec3::new(0.0, -2.0, -13.0), + _padding: 0, + color: glam::Vec3::new(1.0, 1.0, 1.0), + _padding2: 0, + }; + let point_light_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Point Light Buffer"), + contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + } + ); + + let point_light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + } + ], + label: Some("point_light_bind_group_layout"), + }); + + let point_light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &point_light_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: point_light_buffer.as_entire_binding(), + } + ], + label: Some("point_light_bind_group"), + }); + let camera_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Camera Buffer"), - contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]), + contents: bytemuck::cast_slice(&[CameraUniform::default()]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, } ); @@ -248,7 +296,7 @@ impl BasicRenderer { entries: &[ wgpu::BindGroupLayoutEntry { binding: 0, - visibility: wgpu::ShaderStages::VERTEX, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, @@ -323,13 +371,19 @@ impl BasicRenderer { default_texture_bind_group: default_tex_bindgroup, depth_buffer_texture: depth_texture, entity_last_transforms: HashMap::new(), + + point_light_uniform, + point_light_buffer, + point_light_bind_group_layout, + point_light_bind_group, }; // create the default pipelines let mut pipelines = HashMap::new(); pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader, vec![super::vertex::Vertex::desc(),], - vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout]))); + vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout, + &s.point_light_bind_group_layout]))); s.render_pipelines = pipelines; s @@ -376,12 +430,14 @@ impl BasicRenderer { let positions = mesh.position().unwrap(); let tex_coords: Vec = mesh.tex_coords().cloned() .unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]); + let normals = mesh.normals().unwrap(); - assert!(positions.len() == tex_coords.len()); + assert!(positions.len() == tex_coords.len() && positions.len() == normals.len()); - let vertex_inputs: Vec = std::iter::zip(positions, tex_coords) - .map(|(v, t)| Vertex::new(*v, t)) - .collect(); + let mut vertex_inputs = vec![]; + for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) { + vertex_inputs.push(Vertex::new(*v, *t, *n)); + } let vertex_buffer = self.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { @@ -588,10 +644,22 @@ impl Renderer for BasicRenderer { if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() { let view_proj = self.inuse_camera.update_view_projection(camera); - self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[*view_proj])); + let pos = camera.transform.translation; + let uniform = CameraUniform { + view_proj: *view_proj, + view_pos: glam::Vec4::new(pos.x, pos.y, pos.z, 0.0), + }; + self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniform])); } else { warn!("Missing camera!"); } + + { + let old_pos = self.point_light_uniform.position; + self.point_light_uniform.position = glam::Quat::from_axis_angle(glam::Vec3::Z, + math::Angle::Degrees(1.0).to_radians()) * old_pos; + self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[self.point_light_uniform])); + } } fn render(&mut self) -> Result<(), wgpu::SurfaceError> { @@ -650,6 +718,9 @@ impl Renderer for BasicRenderer { // Bind camera render_pass.set_bind_group(2, &self.camera_bind_group, &[]); + + // bind light + render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); // if this mesh uses indices, use them to draw the mesh if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() { diff --git a/src/render/shaders/base.wgsl b/src/render/shaders/base.wgsl index c8e6ce1..fc9d227 100755 --- a/src/render/shaders/base.wgsl +++ b/src/render/shaders/base.wgsl @@ -3,15 +3,24 @@ struct VertexInput { @location(0) position: vec3, @location(1) tex_coords: vec2, + @location(2) normal: vec3, } struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) tex_coords: vec2, + @location(1) world_position: vec3, + @location(2) world_normal: vec3, } struct CameraUniform { view_proj: mat4x4, + view_pos: vec4, +}; + +struct PointLight { + position: vec3, + color: vec3, }; @group(1) @binding(0) @@ -20,13 +29,25 @@ var u_model_transform: mat4x4; @group(2) @binding(0) var camera: CameraUniform; +@group(3) @binding(0) +var point_light: PointLight; + + @vertex fn vs_main( model: VertexInput, ) -> VertexOutput { var out: VertexOutput; + out.tex_coords = model.tex_coords; out.clip_position = camera.view_proj * u_model_transform * vec4(model.position, 1.0); + + out.world_normal = (u_model_transform * vec4(model.normal, 0.0)).xyz; + + //out.world_normal = model.normal; + var world_position: vec4 = u_model_transform * vec4(model.position, 1.0); + out.world_position = world_position.xyz; + return out; } @@ -37,7 +58,35 @@ var t_diffuse: texture_2d; @group(0)@binding(1) var s_diffuse: sampler; +//@group(3) @binding(0) +//var point_light: PointLight; + @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - return textureSample(t_diffuse, s_diffuse, in.tex_coords); + //return textureSample(t_diffuse, s_diffuse, in.tex_coords); + + let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); + + // We don't need (or want) much ambient light, so 0.1 is fine + let ambient_strength = 0.1; + let ambient_color = point_light.color * ambient_strength; + + //// diffuse //// + let light_dir = normalize(point_light.position - in.world_position); + + let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); + let diffuse_color = point_light.color * diffuse_strength; + //// end of diffuse //// + + //// specular //// + let view_dir = normalize(camera.view_pos.xyz - in.world_position); + let half_dir = normalize(view_dir + light_dir); + + let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); + let specular_color = specular_strength * point_light.color; + //// end of specular //// + + let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; + + return vec4(result, object_color.a); } diff --git a/src/render/vertex.rs b/src/render/vertex.rs index 2b53e9d..346fa6d 100755 --- a/src/render/vertex.rs +++ b/src/render/vertex.rs @@ -4,14 +4,15 @@ use super::desc_buf_lay::DescVertexBufferLayout; #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct Vertex { pub position: glam::Vec3, - pub tex_coords: glam::Vec2 + pub tex_coords: glam::Vec2, + pub normals: glam::Vec3, //pub color: [f32; 3], // TODO: add color again } impl Vertex { - pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self { + pub fn new(position: glam::Vec3, tex_coords: glam::Vec2, normals: glam::Vec3) -> Self { Self { - position, tex_coords + position, tex_coords, normals } } } @@ -31,6 +32,11 @@ impl DescVertexBufferLayout for Vertex { offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, shader_location: 1, format: wgpu::VertexFormat::Float32x2, // Vec2 + }, + wgpu::VertexAttribute { + offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 2, + format: wgpu::VertexFormat::Float32x3, // Vec3 } ] } From d26e1ccfb4e0839a4062341e19c3dc8441c0f902 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 10 Nov 2023 12:28:17 -0500 Subject: [PATCH 2/5] Create a PointLight component for light position --- examples/testbed/src/main.rs | 25 +++++++++++++++++++++-- src/render/light/mod.rs | 39 ++++++++++++++++++++++++++++++++++++ src/render/light/point.rs | 9 ++++++--- src/render/renderer.rs | 21 ++++++------------- src/render/shaders/base.wgsl | 35 +++++++++++++++++++++++--------- 5 files changed, 99 insertions(+), 30 deletions(-) diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 710d01d..8014ee8 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -1,4 +1,4 @@ -use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::window::{CursorGrabMode, WindowOptions}, change_tracker::Ct}; +use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::{window::{CursorGrabMode, WindowOptions}, light::PointLight}, change_tracker::Ct}; use lyra_engine::assets::{ResourceManager, Model}; mod free_fly_camera; @@ -69,7 +69,7 @@ async fn main() { let mut resman = world.get_resource_mut::().unwrap(); //let diffuse_texture = resman.request::("assets/happy-tree.png").unwrap(); let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); - //let cube_model = resman.request::("assets/texture-sep/texture-sep.gltf").unwrap(); + let cube_model = resman.request::("assets/cube-texture-bin.glb").unwrap(); drop(resman); /* world.spawn(( @@ -82,6 +82,27 @@ async fn main() { TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)), )); + /* let light = PointLight { + color: Vec3::new(1.0, 1.0, 1.0), + position: Vec3::new(0.0, -5.0, -8.0), + constant: 1.0, + linear: 0.09, + 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, + constant: 1.0, + linear: 0.045, + quadratic: 0.0075, + }, + TransformComponent::from(Transform::from_xyz(pos.x, pos.y, pos.z)), + ModelComponent(cube_model), + )); + let mut camera = CameraComponent::new_3d(); camera.transform.translation += math::Vec3::new(0.0, 0.0, 7.5); //camera.transform.rotate_y(Angle::Degrees(-25.0)); diff --git a/src/render/light/mod.rs b/src/render/light/mod.rs index 178c632..dc9fa9c 100644 --- a/src/render/light/mod.rs +++ b/src/render/light/mod.rs @@ -1,6 +1,8 @@ pub mod point; pub use point::*; +use crate::math::Transform; + #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct LightUniform { @@ -10,4 +12,41 @@ pub struct LightUniform { pub color: glam::Vec3, // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here pub(crate) _padding2: u32, +} + +#[repr(C)] +#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +pub struct PointLightUniform { + /// The position of the light + /// vec4 is used here for gpu padding, w is ignored in the shader + pub position: glam::Vec4, + /// The color of the light + /// vec4 is used here for gpu padding, w is ignored in the shader + pub color: glam::Vec4, + /// The intensity of the light + /// This works by just multiplying the result of the lighting + /// calculations by this scalar + pub intensity: f32, + /// The constant used in the quadratic attenuation calculation. Its best to leave this at 1.0 + pub constant: f32, + /// The linear factor used in the quadratic attenuation calculation. + pub linear: f32, + /// The quadratic factor used in the quadratic attenuation calculation. + pub quadratic: f32, +} + +impl PointLightUniform { + /// Create the PointLightUniform from an ECS bundle + pub fn from_bundle(light: &PointLight, transform: &Transform) -> Self { + Self { + position: glam::Vec4::new(transform.translation.x, transform.translation.y, transform.translation.z, 0.0), + //_padding: 0, + color: glam::Vec4::new(light.color.x, light.color.y, light.color.z, 0.0), + //_padding2: 0, + intensity: light.intensity, + constant: light.constant, + linear: light.linear, + quadratic: light.quadratic, + } + } } \ No newline at end of file diff --git a/src/render/light/point.rs b/src/render/light/point.rs index e7b99a4..a2b1f2c 100644 --- a/src/render/light/point.rs +++ b/src/render/light/point.rs @@ -1,6 +1,9 @@ #[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[derive(Default, Debug, Copy, Clone, edict::Component)] pub struct PointLight { - position: glam::Vec3, - color: glam::Vec3, + pub color: glam::Vec3, + pub intensity: f32, + pub constant: f32, + pub linear: f32, + pub quadratic: f32, } \ No newline at end of file diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 1afffd0..9d86b1e 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -18,10 +18,11 @@ use crate::ecs::components::camera::CameraComponent; use crate::ecs::components::model::ModelComponent; use crate::ecs::components::transform::TransformComponent; use crate::math::{Transform, self}; +use crate::render::light::PointLightUniform; use super::camera::{RenderCamera, CameraUniform}; use super::desc_buf_lay::DescVertexBufferLayout; -use super::light::LightUniform; +use super::light::{LightUniform, PointLight}; use super::texture::RenderTexture; use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers}; use super::vertex::Vertex; @@ -90,7 +91,6 @@ pub struct BasicRenderer { default_texture_bind_group: BindGroup, depth_buffer_texture: RenderTexture, - point_light_uniform: LightUniform, point_light_buffer: wgpu::Buffer, point_light_bind_group_layout: BindGroupLayout, point_light_bind_group: BindGroup, @@ -243,16 +243,10 @@ impl BasicRenderer { next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 }, }; - let point_light_uniform = LightUniform { - position: glam::Vec3::new(0.0, -2.0, -13.0), - _padding: 0, - color: glam::Vec3::new(1.0, 1.0, 1.0), - _padding2: 0, - }; let point_light_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Point Light Buffer"), - contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]), + contents: bytemuck::cast_slice(&[PointLightUniform::default()]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, } ); @@ -372,7 +366,6 @@ impl BasicRenderer { depth_buffer_texture: depth_texture, entity_last_transforms: HashMap::new(), - point_light_uniform, point_light_buffer, point_light_bind_group_layout, point_light_bind_group, @@ -654,11 +647,9 @@ impl Renderer for BasicRenderer { warn!("Missing camera!"); } - { - let old_pos = self.point_light_uniform.position; - self.point_light_uniform.position = glam::Quat::from_axis_angle(glam::Vec3::Z, - math::Angle::Degrees(1.0).to_radians()) * old_pos; - self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[self.point_light_uniform])); + for (point_light, transform) in main_world.query::<(&PointLight, &TransformComponent)>().iter() { + let uniform = PointLightUniform::from_bundle(point_light, &transform.transform); + self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[uniform])); } } diff --git a/src/render/shaders/base.wgsl b/src/render/shaders/base.wgsl index fc9d227..2123c59 100755 --- a/src/render/shaders/base.wgsl +++ b/src/render/shaders/base.wgsl @@ -19,8 +19,12 @@ struct CameraUniform { }; struct PointLight { - position: vec3, - color: vec3, + position: vec4, + color: vec4, + intensity: f32, + constant: f32, + linear: f32, + quadratic: f32, }; @group(1) @binding(0) @@ -32,7 +36,6 @@ var camera: CameraUniform; @group(3) @binding(0) var point_light: PointLight; - @vertex fn vs_main( model: VertexInput, @@ -63,29 +66,41 @@ var s_diffuse: sampler; @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { - //return textureSample(t_diffuse, s_diffuse, in.tex_coords); + let light_color = point_light.color.xyz; + let light_pos = point_light.position.xyz; + let camera_view_pos = camera.view_pos.xyz; let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); - + // We don't need (or want) much ambient light, so 0.1 is fine let ambient_strength = 0.1; - let ambient_color = point_light.color * ambient_strength; + var ambient_color = light_color * ambient_strength; //// diffuse //// - let light_dir = normalize(point_light.position - in.world_position); + let light_dir = normalize(light_pos - in.world_position); let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); - let diffuse_color = point_light.color * diffuse_strength; + var diffuse_color = light_color * diffuse_strength; //// end of diffuse //// //// specular //// - let view_dir = normalize(camera.view_pos.xyz - in.world_position); + let view_dir = normalize(camera_view_pos - in.world_position); let half_dir = normalize(view_dir + light_dir); let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); - let specular_color = specular_strength * point_light.color; + var specular_color = specular_strength * light_color; //// end of specular //// + //// point light attenuation //// + let distance = length(light_pos - in.world_position); + let attenuation = 1.0 / (point_light.constant + point_light.linear * distance + + point_light.quadratic * (distance * distance)); + + ambient_color *= attenuation * point_light.intensity; + diffuse_color *= attenuation * point_light.intensity; + specular_color *= attenuation * point_light.intensity; + //// end of point light attenuation //// + let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; return vec4(result, object_color.a); From 6510d5a7b9278fc177f6d9b5b8f76bbf808212a3 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 10 Nov 2023 14:00:52 -0500 Subject: [PATCH 3/5] Start support for multiple point light casters --- src/render/light/mod.rs | 196 +++++++++++++++++++++++++++++++++-- src/render/renderer.rs | 59 ++--------- src/render/shaders/base.wgsl | 43 ++++---- 3 files changed, 222 insertions(+), 76 deletions(-) diff --git a/src/render/light/mod.rs b/src/render/light/mod.rs index dc9fa9c..358b667 100644 --- a/src/render/light/mod.rs +++ b/src/render/light/mod.rs @@ -1,17 +1,193 @@ pub mod point; +use std::{collections::{VecDeque, HashMap}, num::NonZeroU64}; + +use edict::query::EpochOf; pub use point::*; +use tracing::debug; +use wgpu::util::DeviceExt; -use crate::math::Transform; +use std::mem; -#[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct LightUniform { - pub position: glam::Vec3, - // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here - pub(crate) _padding: u32, - pub color: glam::Vec3, - // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here - pub(crate) _padding2: u32, +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, + /// 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. + pub current_count: usize, + /// The buffer index for a specific entity/caster. + used_indexes: HashMap, + /// Indexes that were being used but are no longer needed. + 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"), + }); + + + Self { + buffer, + bind_group, + device_limits, + max_count, + current_count: 0, + used_indexes: HashMap::new(), + dead_indexes: VecDeque::new(), + } + } + + pub fn has_light(&self, entity: edict::EntityId) -> bool { + self.used_indexes.contains_key(&entity) + } + + /// Update an existing light in the light buffer. + pub fn update_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) { + 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)); + } + + /// Add a new light to the light buffer. + pub fn add_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId, light: &PointLightUniform) { + let buffer_idx = match self.dead_indexes.pop_front() { + Some(i) => i, + None => { + let i = self.current_count; + self.current_count += 1; + i + }, + }; + + self.used_indexes.insert(entity, buffer_idx); + self.update_light(queue, 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) { + if self.used_indexes.contains_key(&entity) { + self.update_light(queue, entity, light); + } else { + self.add_light(queue, entity, light); + } + } + + pub fn remove_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) { + todo!() // TODO + } + + pub fn prune_dead_lights(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) { + todo!() // TODO + } +} + +pub struct LightUniformBuffers { + pub bindgroup_layout: wgpu::BindGroupLayout, + pub point_lights: LightBuffer, + //spotlights: LightBuffer, + //directional_light +} + +impl LightUniformBuffers { + pub fn new(device: &wgpu::Device) -> Self { + let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + } + ], + label: Some("light_bind_group_layout"), + }); + + let point_lights = LightBuffer::new::(device, &bindgroup_layout, 10); + + Self { + bindgroup_layout, + point_lights, + } + } + + pub fn update_lights(&mut self, queue: &wgpu::Queue, world: &edict::World) { + let now_epoch = world.epoch(); + 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!"); + 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); + } + } + + // 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, &[]); + } */ } #[repr(C)] diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 9d86b1e..c657eaa 100755 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -22,7 +22,7 @@ use crate::render::light::PointLightUniform; use super::camera::{RenderCamera, CameraUniform}; use super::desc_buf_lay::DescVertexBufferLayout; -use super::light::{LightUniform, PointLight}; +use super::light::{PointLight, LightUniformBuffers}; use super::texture::RenderTexture; use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers}; use super::vertex::Vertex; @@ -91,9 +91,7 @@ pub struct BasicRenderer { default_texture_bind_group: BindGroup, depth_buffer_texture: RenderTexture, - point_light_buffer: wgpu::Buffer, - point_light_bind_group_layout: BindGroupLayout, - point_light_bind_group: BindGroup, + light_buffers: LightUniformBuffers, } impl BasicRenderer { @@ -243,41 +241,6 @@ impl BasicRenderer { next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 }, }; - let point_light_buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Point Light Buffer"), - contents: bytemuck::cast_slice(&[PointLightUniform::default()]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - } - ); - - let point_light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - } - ], - label: Some("point_light_bind_group_layout"), - }); - - let point_light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &point_light_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: point_light_buffer.as_entire_binding(), - } - ], - label: Some("point_light_bind_group"), - }); - let camera_buffer = device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("Camera Buffer"), @@ -335,6 +298,8 @@ impl BasicRenderer { } ); + let light_uniform_buffers = LightUniformBuffers::new(&device); + let mut s = Self { window, surface, @@ -366,9 +331,7 @@ impl BasicRenderer { depth_buffer_texture: depth_texture, entity_last_transforms: HashMap::new(), - point_light_buffer, - point_light_bind_group_layout, - point_light_bind_group, + light_buffers: light_uniform_buffers, }; // create the default pipelines @@ -376,7 +339,7 @@ impl BasicRenderer { pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader, vec![super::vertex::Vertex::desc(),], vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout, - &s.point_light_bind_group_layout]))); + &s.light_buffers.bindgroup_layout]))); s.render_pipelines = pipelines; s @@ -647,10 +610,7 @@ impl Renderer for BasicRenderer { warn!("Missing camera!"); } - for (point_light, transform) in main_world.query::<(&PointLight, &TransformComponent)>().iter() { - let uniform = PointLightUniform::from_bundle(point_light, &transform.transform); - self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[uniform])); - } + self.light_buffers.update_lights(&self.queue, &main_world); } fn render(&mut self) -> Result<(), wgpu::SurfaceError> { @@ -711,7 +671,10 @@ impl Renderer for BasicRenderer { render_pass.set_bind_group(2, &self.camera_bind_group, &[]); // bind light - render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); + //render_pass.set_bind_group(3, &self.point_light_bind_group, &[]); + render_pass.set_bind_group(3, &self.light_buffers.point_lights.bind_group, &[]); + ////self.light_buffers.bind_lights(&mut render_pass, 3); + // if this mesh uses indices, use them to draw the mesh if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() { diff --git a/src/render/shaders/base.wgsl b/src/render/shaders/base.wgsl index 2123c59..ec7267c 100755 --- a/src/render/shaders/base.wgsl +++ b/src/render/shaders/base.wgsl @@ -1,5 +1,7 @@ // Vertex shader +const max_light_count: i32 = 10; + struct VertexInput { @location(0) position: vec3, @location(1) tex_coords: vec2, @@ -31,10 +33,10 @@ struct PointLight { var u_model_transform: mat4x4; @group(2) @binding(0) -var camera: CameraUniform; +var u_camera: CameraUniform; @group(3) @binding(0) -var point_light: PointLight; +var u_point_lights: array; @vertex fn vs_main( @@ -43,7 +45,7 @@ fn vs_main( var out: VertexOutput; out.tex_coords = model.tex_coords; - out.clip_position = camera.view_proj * u_model_transform * vec4(model.position, 1.0); + out.clip_position = u_camera.view_proj * u_model_transform * vec4(model.position, 1.0); out.world_normal = (u_model_transform * vec4(model.normal, 0.0)).xyz; @@ -61,38 +63,45 @@ var t_diffuse: texture_2d; @group(0)@binding(1) var s_diffuse: sampler; -//@group(3) @binding(0) -//var point_light: PointLight; - @fragment 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]); + } + let light_object_res = light_res * object_color.xyz; + + return vec4(light_object_res, object_color.a); +} + +fn blinn_phong_point_light(world_pos: vec3, world_norm: vec3, point_light: PointLight) -> vec3 { let light_color = point_light.color.xyz; let light_pos = point_light.position.xyz; - let camera_view_pos = camera.view_pos.xyz; - - let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); + let camera_view_pos = u_camera.view_pos.xyz; // We don't need (or want) much ambient light, so 0.1 is fine let ambient_strength = 0.1; var ambient_color = light_color * ambient_strength; //// diffuse //// - let light_dir = normalize(light_pos - in.world_position); + let light_dir = normalize(light_pos - world_pos); - let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); + let diffuse_strength = max(dot(world_norm, light_dir), 0.0); var diffuse_color = light_color * diffuse_strength; //// end of diffuse //// //// specular //// - let view_dir = normalize(camera_view_pos - in.world_position); + let view_dir = normalize(camera_view_pos - world_pos); let half_dir = normalize(view_dir + light_dir); - let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); + let specular_strength = pow(max(dot(world_norm, half_dir), 0.0), 32.0); var specular_color = specular_strength * light_color; //// end of specular //// //// point light attenuation //// - let distance = length(light_pos - in.world_position); + let distance = length(light_pos - world_pos); let attenuation = 1.0 / (point_light.constant + point_light.linear * distance + point_light.quadratic * (distance * distance)); @@ -101,7 +110,5 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { specular_color *= attenuation * point_light.intensity; //// end of point light attenuation //// - let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; - - return vec4(result, object_color.a); -} + return (ambient_color + diffuse_color + specular_color); +} \ No newline at end of file From aa79ace4017148a6ee6bf40ef19302d30f2bb458 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 10 Nov 2023 17:52:11 -0500 Subject: [PATCH 4/5] 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; From da1cac1bff7c77971aa28dfb98b11d834d43d599 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 10 Nov 2023 18:10:00 -0500 Subject: [PATCH 5/5] Implement removing a light from the scene --- src/render/light/mod.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/render/light/mod.rs b/src/render/light/mod.rs index c1c51fe..8f084d1 100644 --- a/src/render/light/mod.rs +++ b/src/render/light/mod.rs @@ -71,12 +71,17 @@ impl LightBuffer { } } - pub fn remove_light(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) { - todo!() // TODO - } + /// Remove a caster from the buffer, returns true if it was removed. + pub fn remove_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId) -> bool { + if let Some(removed_idx) = self.used_indexes.remove(&entity) { + self.dead_indexes.push_back(removed_idx); + self.current_count -= 1; + lights_buffer[removed_idx] = U::default(); - pub fn prune_dead_lights(&mut self, queue: &wgpu::Queue, entity: edict::EntityId) { - todo!() // TODO + true + } else { + false + } } }