From ce1773894f17545c01bef19c130f5df722b31e52 Mon Sep 17 00:00:00 2001 From: Verox001 Date: Thu, 16 Jan 2025 12:53:05 +0100 Subject: [PATCH] Added light point --- src/lib.rs | 237 +++++++++++++++++++++++++++++++++++++------------ src/light.wgsl | 42 +++++++++ src/model.rs | 111 +++++++++++++++++++++-- 3 files changed, 325 insertions(+), 65 deletions(-) create mode 100644 src/light.wgsl diff --git a/src/lib.rs b/src/lib.rs index 66fc220..129f9fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,17 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( const NUM_INSTANCES_PER_ROW: u32 = 10; +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct LightUniform { + position: [f32; 3], + // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here + _padding: u32, + color: [f32; 3], + // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here + _padding2: u32, +} + #[derive(Debug)] struct Camera { eye: cgmath::Point3, @@ -238,6 +249,72 @@ struct State<'a> { instance_buffer: wgpu::Buffer, depth_texture: texture::Texture, window: &'a Window, + light_uniform: LightUniform, + light_buffer: wgpu::Buffer, + light_bind_group_layout: wgpu::BindGroupLayout, + light_bind_group: wgpu::BindGroup, + light_render_pipeline: wgpu::RenderPipeline, +} + +fn create_render_pipeline( + device: &wgpu::Device, + layout: &wgpu::PipelineLayout, + color_format: wgpu::TextureFormat, + depth_format: Option, + vertex_layouts: &[wgpu::VertexBufferLayout], + shader: wgpu::ShaderModuleDescriptor, +) -> wgpu::RenderPipeline { + let shader = device.create_shader_module(shader); + + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + cache: None, + layout: Some(layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: vertex_layouts, + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: color_format, + blend: Some(wgpu::BlendState { + alpha: wgpu::BlendComponent::REPLACE, + color: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE + polygon_mode: wgpu::PolygonMode::Fill, + // Requires Features::DEPTH_CLIP_CONTROL + unclipped_depth: false, + // Requires Features::CONSERVATIVE_RASTERIZATION + conservative: false, + }, + depth_stencil: depth_format.map(|format| wgpu::DepthStencilState { + format, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }) } impl<'a> State<'a> { @@ -412,67 +489,91 @@ impl<'a> State<'a> { let depth_texture = texture::Texture::create_depth_texture(&device, &config, "depth_texture"); - let render_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout], - push_constant_ranges: &[], + let light_uniform = LightUniform { + position: [2.0, 2.0, 2.0], + _padding: 0, + color: [1.0, 1.0, 1.0], + _padding2: 0, + }; + + // We'll want to update our lights position, so we use COPY_DST + let light_buffer = device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Light VB"), + contents: bytemuck::cast_slice(&[light_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + } + ); + + let 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: None, }); - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[model::ModelVertex::desc(), InstanceRaw::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent::REPLACE, - alpha: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::POLYGON_MODE_LINE - // or Features::POLYGON_MODE_POINT - polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLIP_CONTROL - unclipped_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION - conservative: false, - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: texture::Texture::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - // If the pipeline will be used with a multiview render pass, this - // indicates how many array layers the attachments will have. - multiview: None, - // Useful for optimizing shader compilation on Android - cache: None, + let light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &light_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: light_buffer.as_entire_binding(), + }], + label: None, }); + let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[ + &texture_bind_group_layout, + &camera_bind_group_layout, + &light_bind_group_layout, + ], + label: Some("Render Pipeline Layout"), + push_constant_ranges: &[], + }); + + let render_pipeline = { + let shader = wgpu::ShaderModuleDescriptor { + label: Some("Normal Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }; + create_render_pipeline( + &device, + &render_pipeline_layout, + config.format, + Some(texture::Texture::DEPTH_FORMAT), + &[model::ModelVertex::desc(), InstanceRaw::desc()], + shader, + ) + }; + + let light_render_pipeline = { + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Light Pipeline Layout"), + bind_group_layouts: &[&camera_bind_group_layout, &light_bind_group_layout], + push_constant_ranges: &[], + }); + let shader = wgpu::ShaderModuleDescriptor { + label: Some("Light Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("light.wgsl").into()), + }; + create_render_pipeline( + &device, + &layout, + config.format, + Some(texture::Texture::DEPTH_FORMAT), + &[model::ModelVertex::desc()], + shader, + ) + }; + Self { surface, device, @@ -490,6 +591,11 @@ impl<'a> State<'a> { instance_buffer, depth_texture, window, + light_uniform, + light_buffer, + light_bind_group_layout, + light_bind_group, + light_render_pipeline, } } @@ -522,6 +628,13 @@ impl<'a> State<'a> { 0, bytemuck::cast_slice(&[self.camera_uniform]), ); + + let old_position: cgmath::Vector3<_> = self.light_uniform.position.into(); + self.light_uniform.position = + (cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) + * old_position) + .into(); + self.queue.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light_uniform])); } fn render(&mut self) -> Result<(), wgpu::SurfaceError> { @@ -565,11 +678,21 @@ impl<'a> State<'a> { }); render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); + + use crate::model::DrawLight; + render_pass.set_pipeline(&self.light_render_pipeline); + render_pass.draw_light_model( + &self.obj_model, + &self.camera_bind_group, + &self.light_bind_group, + ); + render_pass.set_pipeline(&self.render_pipeline); render_pass.draw_model_instanced( &self.obj_model, 0..self.instances.len() as u32, &self.camera_bind_group, + &self.light_bind_group, ); } diff --git a/src/light.wgsl b/src/light.wgsl new file mode 100644 index 0000000..5ecede8 --- /dev/null +++ b/src/light.wgsl @@ -0,0 +1,42 @@ +// light.wgsl +// Vertex shader + +struct Camera { + view_proj: mat4x4, +} +@group(0) @binding(0) +var camera: Camera; + +struct Light { + position: vec3, + color: vec3, +} +@group(1) @binding(0) +var light: Light; + +struct VertexInput { + @location(0) position: vec3, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) color: vec3, +}; + +@vertex +fn vs_main( + model: VertexInput, +) -> VertexOutput { + let scale = 0.25; + var out: VertexOutput; + out.clip_position = camera.view_proj * vec4(model.position * scale + light.position, 1.0); + out.color = light.color; + return out; +} + +// Fragment shader + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return vec4(in.color, 1.0); +} \ No newline at end of file diff --git a/src/model.rs b/src/model.rs index 70fffb1..442fc6a 100644 --- a/src/model.rs +++ b/src/model.rs @@ -63,13 +63,14 @@ pub struct Model { pub materials: Vec, } +// model.rs pub trait DrawModel<'a> { - #[allow(unused)] fn draw_mesh( &mut self, mesh: &'a Mesh, material: &'a Material, camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, ); fn draw_mesh_instanced( &mut self, @@ -77,15 +78,21 @@ pub trait DrawModel<'a> { material: &'a Material, instances: Range, camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, ); - #[allow(unused)] - fn draw_model(&mut self, model: &'a Model, camera_bind_group: &'a wgpu::BindGroup); + fn draw_model( + &mut self, + model: &'a Model, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); fn draw_model_instanced( &mut self, model: &'a Model, instances: Range, camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, ); } @@ -98,8 +105,9 @@ where mesh: &'b Mesh, material: &'b Material, camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, ) { - self.draw_mesh_instanced(mesh, material, 0..1, camera_bind_group); + self.draw_mesh_instanced(mesh, material, 0..1, camera_bind_group, light_bind_group); } fn draw_mesh_instanced( @@ -108,16 +116,23 @@ where material: &'b Material, instances: Range, camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, ) { self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); self.set_bind_group(0, &material.bind_group, &[]); self.set_bind_group(1, camera_bind_group, &[]); + self.set_bind_group(2, light_bind_group, &[]); self.draw_indexed(0..mesh.num_elements, 0, instances); } - fn draw_model(&mut self, model: &'b Model, camera_bind_group: &'b wgpu::BindGroup) { - self.draw_model_instanced(model, 0..1, camera_bind_group); + fn draw_model( + &mut self, + model: &'b Model, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_model_instanced(model, 0..1, camera_bind_group, light_bind_group); } fn draw_model_instanced( @@ -125,10 +140,90 @@ where model: &'b Model, instances: Range, camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, ) { for mesh in &model.meshes { let material = &model.materials[mesh.material]; - self.draw_mesh_instanced(mesh, material, instances.clone(), camera_bind_group); + self.draw_mesh_instanced(mesh, material, instances.clone(), camera_bind_group, light_bind_group); } } -} \ No newline at end of file +} + +// model.rs +pub trait DrawLight<'a> { + fn draw_light_mesh( + &mut self, + mesh: &'a Mesh, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_light_mesh_instanced( + &mut self, + mesh: &'a Mesh, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + + fn draw_light_model( + &mut self, + model: &'a Model, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); + fn draw_light_model_instanced( + &mut self, + model: &'a Model, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + ); +} + +impl<'a, 'b> DrawLight<'b> for wgpu::RenderPass<'a> +where + 'b: 'a, +{ + fn draw_light_mesh( + &mut self, + mesh: &'b Mesh, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_light_mesh_instanced(mesh, 0..1, camera_bind_group, light_bind_group); + } + + fn draw_light_mesh_instanced( + &mut self, + mesh: &'b Mesh, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); + self.set_bind_group(0, camera_bind_group, &[]); + self.set_bind_group(1, light_bind_group, &[]); + self.draw_indexed(0..mesh.num_elements, 0, instances); + } + + fn draw_light_model( + &mut self, + model: &'b Model, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + self.draw_light_model_instanced(model, 0..1, camera_bind_group, light_bind_group); + } + fn draw_light_model_instanced( + &mut self, + model: &'b Model, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + ) { + for mesh in &model.meshes { + self.draw_light_mesh_instanced(mesh, instances.clone(), camera_bind_group, light_bind_group); + } + } +}