From ac67a3d4de5e7b9521964d551a2a99e383ce9fc0 Mon Sep 17 00:00:00 2001 From: nullprop Date: Sun, 29 Jan 2023 18:57:29 +0200 Subject: [PATCH] WIP shadowmapping --- res/shaders/depth.wgsl | 17 +++++ res/shaders/globals.wgsl | 51 ++++++++++++++ res/shaders/light.wgsl | 33 +++------ res/shaders/pbr.wgsl | 58 +++------------- src/core/camera.rs | 37 ++++++++-- src/core/model.rs | 8 +-- src/core/pass.rs | 43 +++++++----- src/core/state.rs | 142 +++++++++++++++++++++++++++++++++------ src/core/texture.rs | 3 +- 9 files changed, 270 insertions(+), 122 deletions(-) create mode 100644 res/shaders/depth.wgsl create mode 100644 res/shaders/globals.wgsl diff --git a/res/shaders/depth.wgsl b/res/shaders/depth.wgsl new file mode 100644 index 0000000..b84d21c --- /dev/null +++ b/res/shaders/depth.wgsl @@ -0,0 +1,17 @@ +#include globals.wgsl + +@vertex +fn vs_main( + model: VertexInput, + instance: InstanceInput, +) -> @builtin(position) vec4 { + let model_matrix = mat4x4( + instance.model_matrix_0, + instance.model_matrix_1, + instance.model_matrix_2, + instance.model_matrix_3, + ); + + let world_position = model_matrix * vec4(model.position, 1.0); + return camera.proj * camera.view * world_position; +} diff --git a/res/shaders/globals.wgsl b/res/shaders/globals.wgsl new file mode 100644 index 0000000..c5cdab8 --- /dev/null +++ b/res/shaders/globals.wgsl @@ -0,0 +1,51 @@ +// Vertex shader + +struct CameraUniform { + view: mat4x4, + proj: mat4x4, + position: vec4, +} +@group(0) @binding(0) +var camera: CameraUniform; + +struct Light { + position: vec3, + color: vec4, +} +@group(1) @binding(0) +var light: Light; + +struct VertexInput { + @location(0) position: vec3, + @location(1) tex_coords: vec2, + @location(2) normal: vec3, + @location(3) tangent: vec3, + @location(4) bitangent: vec3, +} + +struct InstanceInput { + @location(5) model_matrix_0: vec4, + @location(6) model_matrix_1: vec4, + @location(7) model_matrix_2: vec4, + @location(8) model_matrix_3: vec4, + @location(9) normal_matrix_0: vec3, + @location(10) normal_matrix_1: vec3, + @location(11) normal_matrix_2: vec3, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, + @location(1) tangent_position: vec3, + @location(2) tangent_light_position: vec3, + @location(3) tangent_view_position: vec3, + @location(4) world_position: vec3, + @location(5) light_local_position: vec4, +} + +// Fragment shader + +@group(1)@binding(1) +var t_light_depth: texture_depth_2d; +@group(1) @binding(2) +var s_light_depth: sampler_comparison; \ No newline at end of file diff --git a/res/shaders/light.wgsl b/res/shaders/light.wgsl index db3b8d5..83e28e3 100644 --- a/res/shaders/light.wgsl +++ b/res/shaders/light.wgsl @@ -1,43 +1,26 @@ -// Vertex shader +#include globals.wgsl -struct Camera { - view: mat4x4, - proj: mat4x4, - position: vec4, -} -@group(0) @binding(0) -var camera: Camera; - -struct Light { - position: vec3, - color: vec3, -} -@group(1) @binding(0) -var light: Light; - -struct VertexInput { +struct LightVertexInput { @location(0) position: vec3, }; -struct VertexOutput { +struct LightVertexOutput { @builtin(position) clip_position: vec4, @location(0) color: vec3, }; @vertex fn vs_main( - model: VertexInput, -) -> VertexOutput { + model: LightVertexInput, +) -> LightVertexOutput { let scale = 10.0; - var out: VertexOutput; + var out: LightVertexOutput; out.clip_position = camera.proj * camera.view * vec4(model.position * scale + light.position, 1.0); - out.color = light.color; + out.color = light.color.xyz; return out; } -// Fragment shader - @fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4 { +fn fs_main(in: LightVertexOutput) -> @location(0) vec4 { return vec4(in.color, 1.0); } diff --git a/res/shaders/pbr.wgsl b/res/shaders/pbr.wgsl index 5e6ed7c..e63475c 100644 --- a/res/shaders/pbr.wgsl +++ b/res/shaders/pbr.wgsl @@ -1,50 +1,9 @@ #include constants.wgsl +#include globals.wgsl #include brdf.wgsl // Vertex shader -struct CameraUniform { - view: mat4x4, - proj: mat4x4, - position: vec4, -} -@group(1) @binding(0) -var camera: CameraUniform; - -struct Light { - position: vec3, - color: vec4, -} -@group(2) @binding(0) -var light: Light; - -struct VertexInput { - @location(0) position: vec3, - @location(1) tex_coords: vec2, - @location(2) normal: vec3, - @location(3) tangent: vec3, - @location(4) bitangent: vec3, -} - -struct InstanceInput { - @location(5) model_matrix_0: vec4, - @location(6) model_matrix_1: vec4, - @location(7) model_matrix_2: vec4, - @location(8) model_matrix_3: vec4, - @location(9) normal_matrix_0: vec3, - @location(10) normal_matrix_1: vec3, - @location(11) normal_matrix_2: vec3, -} - -struct VertexOutput { - @builtin(position) clip_position: vec4, - @location(0) tex_coords: vec2, - @location(1) tangent_position: vec3, - @location(2) tangent_light_position: vec3, - @location(3) tangent_view_position: vec3, - @location(4) world_position: vec3, -} - @vertex fn vs_main( model: VertexInput, @@ -81,25 +40,26 @@ fn vs_main( out.tangent_view_position = tangent_matrix * camera.position.xyz; out.world_position = world_position.xyz; + out.light_local_position = camera.proj * camera.view * world_position; return out; } // Fragment shader -@group(0) @binding(0) +@group(2) @binding(0) var t_diffuse: texture_2d; -@group(0)@binding(1) +@group(2)@binding(1) var s_diffuse: sampler; -@group(0)@binding(2) +@group(2)@binding(2) var t_normal: texture_2d; -@group(0) @binding(3) +@group(2) @binding(3) var s_normal: sampler; -@group(0)@binding(4) +@group(2)@binding(4) var t_roughness_metalness: texture_2d; -@group(0) @binding(5) +@group(2) @binding(5) var s_roughness_metalness: sampler; @fragment @@ -109,7 +69,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let object_normal: vec4 = textureSample(t_normal, s_normal, in.tex_coords); let object_roughness_metalness: vec4 = textureSample( t_roughness_metalness, s_roughness_metalness, in.tex_coords); - // TODO: AO + //let light_depth = textureSampleCompareLevel(t_light_depth, s_light_depth, in.light_local_position.xy, 1.0); let albedo = object_color.xyz; // TODO: pass factors to shader diff --git a/src/core/camera.rs b/src/core/camera.rs index ca7c0a2..31f97b8 100644 --- a/src/core/camera.rs +++ b/src/core/camera.rs @@ -7,17 +7,17 @@ pub struct Camera { pub position: cgmath::Point3, pub pitch: f32, pub yaw: f32, - pub projection: Projection, + pub projection: PerspectiveProjection, } -pub struct Projection { +pub struct PerspectiveProjection { pub aspect: f32, pub fovy: f32, pub znear: f32, pub zfar: f32, } -impl Projection { +impl PerspectiveProjection { pub fn resize(&mut self, width: u32, height: u32) { self.aspect = width as f32 / height as f32; } @@ -27,6 +27,35 @@ impl Projection { } } +pub struct OrthoProjection { + pub left: f32, + pub right: f32, + pub bottom: f32, + pub top: f32, + pub znear: f32, + pub zfar: f32, +} + +impl OrthoProjection { + pub fn resize(&mut self, width: u32, height: u32) { + self.left = width as f32 * -0.5; + self.right = width as f32 * 0.5; + self.bottom = height as f32 * -0.5; + self.top = height as f32 * 0.5; + } + + pub fn get_matrix(&self) -> cgmath::Matrix4 { + cgmath::ortho( + self.left, + self.right, + self.bottom, + self.top, + self.znear, + self.zfar, + ) + } +} + impl Camera { pub fn new( position: cgmath::Point3, @@ -39,7 +68,7 @@ impl Camera { position, pitch, yaw, - projection: Projection { + projection: PerspectiveProjection { aspect, fovy, znear: 0.1, diff --git a/src/core/model.rs b/src/core/model.rs index 464eb0e..bac5817 100644 --- a/src/core/model.rs +++ b/src/core/model.rs @@ -7,7 +7,7 @@ pub struct Material { pub diffuse_texture: Texture, pub normal_texture: Texture, pub metallic_roughness_texture: Texture, - pub metallic_factor: f32, // TODO pass to shader + pub metallic_factor: f32, // TODO pass to shader pub roughness_factor: f32, // TODO pass to shader pub bind_group: wgpu::BindGroup, } @@ -194,9 +194,9 @@ where ) { 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.set_bind_group(0, camera_bind_group, &[]); + self.set_bind_group(1, light_bind_group, &[]); + self.set_bind_group(2, &material.bind_group, &[]); self.draw_indexed(0..mesh.num_elements, 0, instances); } diff --git a/src/core/pass.rs b/src/core/pass.rs index b216b0b..7bdba74 100644 --- a/src/core/pass.rs +++ b/src/core/pass.rs @@ -14,17 +14,18 @@ impl RenderPass { bind_group_layouts: &[&BindGroupLayout], push_constant_ranges: &[PushConstantRange], shader_name: &str, - color_format: TextureFormat, + color_format: Option, depth_format: Option, vertex_layouts: &[VertexBufferLayout], + label: &str, ) -> Self { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Pipeline Layout"), + label: Some((label.to_owned() + " pipeline Layout").as_str()), bind_group_layouts: bind_group_layouts, push_constant_ranges: push_constant_ranges, }); let shader = wgpu::ShaderModuleDescriptor { - label: Some("Shader"), + label: Some(shader_name), source: preprocess_wgsl(shader_name), }; let pipeline = Self::create_render_pipeline( @@ -34,6 +35,7 @@ impl RenderPass { depth_format, vertex_layouts, shader, + label, ); Self { pipeline } @@ -42,33 +44,40 @@ impl RenderPass { fn create_render_pipeline( device: &wgpu::Device, layout: &wgpu::PipelineLayout, - color_format: wgpu::TextureFormat, + color_format: Option, depth_format: Option, vertex_layouts: &[wgpu::VertexBufferLayout], shader: wgpu::ShaderModuleDescriptor, + label: &str, ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(shader); + let fragment_targets = &[Some(wgpu::ColorTargetState { + format: color_format.unwrap_or(wgpu::TextureFormat::Bgra8Unorm), + blend: Some(wgpu::BlendState { + alpha: wgpu::BlendComponent::REPLACE, + color: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::ALL, + })]; + let fragment = match color_format { + Some(..) => Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: fragment_targets, + }), + None => None, + }; + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), + label: Some((label.to_owned() + " pipeline Layout").as_str()), layout: Some(layout), vertex: wgpu::VertexState { module: &shader, entry_point: "vs_main", buffers: vertex_layouts, }, - 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, - })], - }), + fragment: fragment, primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, diff --git a/src/core/state.rs b/src/core/state.rs index 1eb602f..27e09e9 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -34,6 +34,8 @@ pub struct State { light_buffer: wgpu::Buffer, light_debug_pass: RenderPass, light_bind_group: wgpu::BindGroup, + light_depth_pass: RenderPass, + light_depth_texture: Texture, } impl State { @@ -121,6 +123,19 @@ impl State { let camera_controller = CameraController::new(400.0, 2.0); + let depth_texture = Texture::create_depth_texture( + &device, + &config, + "depth_texture", + Some(wgpu::CompareFunction::LessEqual), + ); + let light_depth_texture = Texture::create_depth_texture( + &device, + &config, + "light_depth_texture", + Some(wgpu::CompareFunction::LessEqual), + ); + let light_uniform = LightUniform::new([100.0, 60.0, 0.0], [1.0, 1.0, 1.0, 200000.0]); // We'll want to update our lights position, so we use COPY_DST @@ -132,26 +147,55 @@ impl State { 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, + 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, }, - count: None, - }], - label: None, + // depth + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Depth, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison), + count: None, + }, + ], + label: Some("Light Bind Group Layout"), }); 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, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: light_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&light_depth_texture.view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::Sampler(&light_depth_texture.sampler), + }, + ], + label: Some("Light Bind Group"), }); surface.configure(&device, &config); @@ -244,20 +288,19 @@ impl State { usage: wgpu::BufferUsages::VERTEX, }); - let depth_texture = Texture::create_depth_texture(&device, &config, "depth_texture"); - let geometry_pass = RenderPass::new( &device, &[ - &texture_bind_group_layout, &camera_bind_group_layout, &light_bind_group_layout, + &texture_bind_group_layout, ], &[], "pbr.wgsl", - config.format, + Some(config.format), Some(Texture::DEPTH_FORMAT), &[ModelVertex::desc(), InstanceRaw::desc()], + "geometry pass", ); let light_debug_pass = RenderPass::new( @@ -265,9 +308,25 @@ impl State { &[&camera_bind_group_layout, &light_bind_group_layout], &[], "light.wgsl", - config.format, + Some(config.format), Some(Texture::DEPTH_FORMAT), &[ModelVertex::desc()], + "light debug pass", + ); + + let light_depth_pass = RenderPass::new( + &device, + &[ + &camera_bind_group_layout, + &light_bind_group_layout, + &texture_bind_group_layout, + ], + &[], + "depth.wgsl", + None, + Some(Texture::DEPTH_FORMAT), + &[ModelVertex::desc(), InstanceRaw::desc()], + "light depth pass", ); Self { @@ -291,6 +350,8 @@ impl State { light_buffer, light_debug_pass, light_bind_group, + light_depth_pass, + light_depth_texture, } } @@ -303,8 +364,18 @@ impl State { self.camera .projection .resize(new_size.width, new_size.height); - self.depth_texture = - Texture::create_depth_texture(&self.device, &self.config, "depth_texture"); + self.depth_texture = Texture::create_depth_texture( + &self.device, + &self.config, + "depth_texture", + Some(wgpu::CompareFunction::LessEqual), + ); + self.light_depth_texture = Texture::create_depth_texture( + &self.device, + &self.config, + "light_depth_texture", + Some(wgpu::CompareFunction::LessEqual), + ); } } @@ -351,6 +422,33 @@ impl State { label: Some("Render Encoder"), }); + /* + { + let mut light_depth_render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Light Depth Render Pass"), + color_attachments: &[], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &self.light_depth_texture.view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }), + }); + + light_depth_render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); + light_depth_render_pass.set_pipeline(&self.light_depth_pass.pipeline); + light_depth_render_pass.draw_model_instanced( + &self.model, + 0..self.instances.len() as u32, + &self.camera_bind_group, // TODO light projection + &self.light_bind_group, // TODO remove + ); + } + */ + { let mut geom_render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Geometry Render Pass"), diff --git a/src/core/texture.rs b/src/core/texture.rs index f56640a..423a3cc 100644 --- a/src/core/texture.rs +++ b/src/core/texture.rs @@ -13,6 +13,7 @@ impl Texture { device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, label: &str, + compare: Option, ) -> Self { let size = wgpu::Extent3d { width: config.width, @@ -38,7 +39,7 @@ impl Texture { mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), + compare, lod_min_clamp: -100.0, lod_max_clamp: 100.0, ..Default::default()