From c5e32a830dc0ba9ad9c8f6d88e314d42209a5c69 Mon Sep 17 00:00:00 2001 From: nullprop Date: Mon, 30 Jan 2023 00:40:50 +0200 Subject: [PATCH] WIP shadowmapping --- res/shaders/depth.wgsl | 2 +- res/shaders/globals.wgsl | 9 +++-- res/shaders/pbr.wgsl | 87 ++++++++++++++++++++++++++-------------- src/core/camera.rs | 29 -------------- src/core/light.rs | 19 +++++++++ src/core/state.rs | 86 +++++++++++++++++++++++++++------------ src/core/texture.rs | 4 +- 7 files changed, 145 insertions(+), 91 deletions(-) diff --git a/res/shaders/depth.wgsl b/res/shaders/depth.wgsl index b84d21c..836b945 100644 --- a/res/shaders/depth.wgsl +++ b/res/shaders/depth.wgsl @@ -13,5 +13,5 @@ fn vs_main( ); let world_position = model_matrix * vec4(model.position, 1.0); - return camera.proj * camera.view * world_position; + return light.matrices[light.active_matrix] * world_position; } diff --git a/res/shaders/globals.wgsl b/res/shaders/globals.wgsl index c5cdab8..bef15c5 100644 --- a/res/shaders/globals.wgsl +++ b/res/shaders/globals.wgsl @@ -11,6 +11,8 @@ var camera: CameraUniform; struct Light { position: vec3, color: vec4, + matrices: array, 6>, + active_matrix: u32, } @group(1) @binding(0) var light: Light; @@ -39,13 +41,12 @@ struct VertexOutput { @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, + @location(4) world_position: vec4, } // Fragment shader @group(1)@binding(1) -var t_light_depth: texture_depth_2d; +var t_light_depth: binding_array; @group(1) @binding(2) -var s_light_depth: sampler_comparison; \ No newline at end of file +var s_light_depth: binding_array; diff --git a/res/shaders/pbr.wgsl b/res/shaders/pbr.wgsl index e63475c..950c7e4 100644 --- a/res/shaders/pbr.wgsl +++ b/res/shaders/pbr.wgsl @@ -39,8 +39,7 @@ fn vs_main( out.tangent_light_position = tangent_matrix * light.position; 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; + out.world_position = world_position; return out; } @@ -62,46 +61,74 @@ var t_roughness_metalness: texture_2d; @group(2) @binding(5) var s_roughness_metalness: sampler; +fn sample_direct_light(index: i32, light_coords: vec4) -> f32 { + if (light_coords.w <= 0.0) { + return 1.0; + } + + let flip_correction = vec2(0.5, -0.5); + let proj_correction = 1.0 / light_coords.w; + let light_local = light_coords.xy * flip_correction * proj_correction + vec2(0.5, 0.5); + + return textureSampleCompareLevel( + t_light_depth[index], + s_light_depth[index], + light_local, + light_coords.z * proj_correction + ); +} + @fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4 { +fn fs_main(vert: VertexOutput) -> @location(0) vec4 { // textures - let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); - let object_normal: vec4 = textureSample(t_normal, s_normal, in.tex_coords); + let object_color: vec4 = textureSample(t_diffuse, s_diffuse, vert.tex_coords); + let object_normal: vec4 = textureSample(t_normal, s_normal, vert.tex_coords); let object_roughness_metalness: vec4 = textureSample( - t_roughness_metalness, s_roughness_metalness, in.tex_coords); - //let light_depth = textureSampleCompareLevel(t_light_depth, s_light_depth, in.light_local_position.xy, 1.0); + t_roughness_metalness, s_roughness_metalness, vert.tex_coords); let albedo = object_color.xyz; // TODO: pass factors to shader let roughness = object_roughness_metalness.y * 1.0; let metalness = object_roughness_metalness.z * 1.0; - // lighting vecs - let normal_dir = object_normal.xyz * 2.0 - 1.0; - var light_dir = normalize(in.tangent_light_position - in.tangent_position); - let view_dir = normalize(in.tangent_view_position - in.tangent_position); - let half_dir = normalize(view_dir + light_dir); + var total_radiance: vec3; - // attenuation - let light_dist = length(light.position - in.world_position); - let coef_a = 0.0; - let coef_b = 1.0; - let light_attenuation = 1.0 / (1.0 + coef_a * light_dist + coef_b * light_dist * light_dist); + var in_light = 0.0; + for (var i: i32 = 0; i < 6; i++) { + in_light = sample_direct_light(i, light.matrices[i] * vert.world_position); + if (in_light > 0.0) { + break; + } + } - // radiance - let radiance_strength = max(dot(normal_dir, light_dir), 0.0); - let radiance = radiance_strength * light.color.xyz * light.color.w * light_attenuation; + if (in_light > 0.0) { + // lighting vecs + let normal_dir = object_normal.xyz * 2.0 - 1.0; + var light_dir = normalize(vert.tangent_light_position - vert.tangent_position); + let view_dir = normalize(vert.tangent_view_position - vert.tangent_position); + let half_dir = normalize(view_dir + light_dir); - // brdf shading - let total_radiance = radiance * brdf( - normal_dir, - light_dir, - view_dir, - half_dir, - albedo, - roughness, - metalness - ); + // attenuation + let light_dist = length(light.position - vert.world_position.xyz); + let coef_a = 0.0; + let coef_b = 1.0; + let light_attenuation = 1.0 / (1.0 + coef_a * light_dist + coef_b * light_dist * light_dist); + + // radiance + let radiance_strength = max(dot(normal_dir, light_dir), 0.0); + let radiance = radiance_strength * light.color.xyz * light.color.w * light_attenuation; + + // brdf shading + total_radiance += radiance * brdf( + normal_dir, + light_dir, + view_dir, + half_dir, + albedo, + roughness, + metalness + ); + } // ambient let ambient_strength = 0.01; diff --git a/src/core/camera.rs b/src/core/camera.rs index 31f97b8..18889c8 100644 --- a/src/core/camera.rs +++ b/src/core/camera.rs @@ -27,35 +27,6 @@ impl PerspectiveProjection { } } -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, diff --git a/src/core/light.rs b/src/core/light.rs index 157896c..e6ffa42 100644 --- a/src/core/light.rs +++ b/src/core/light.rs @@ -2,20 +2,39 @@ use std::ops::Range; use super::model::{Mesh, Model}; +use cgmath::{Matrix4, Vector3}; + #[repr(C)] #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] pub struct LightUniform { pub position: [f32; 3], _padding: u32, pub color: [f32; 4], + pub matrices: [[[f32; 4]; 4]; 6], + pub active_matrix: u32, + _padding2: [u32; 3], } impl LightUniform { pub fn new(position: [f32; 3], color: [f32; 4]) -> Self { + let proj = cgmath::perspective(cgmath::Deg(90.0), 1.0, 0.1, 1000.0); + #[rustfmt::skip] + let matrices: [[[f32; 4]; 4]; 6] = [ + (proj * Matrix4::look_to_rh(position.into(), Into::>::into(position) + Vector3::new( 1.0, 0.0, 0.0), Vector3::new(0.0,-1.0, 0.0))).into(), + (proj * Matrix4::look_to_rh(position.into(), Into::>::into(position) + Vector3::new(-1.0, 0.0, 0.0), Vector3::new(0.0,-1.0, 0.0))).into(), + (proj * Matrix4::look_to_rh(position.into(), Into::>::into(position) + Vector3::new( 0.0, 1.0, 0.0), Vector3::new(0.0, 0.0, 1.0))).into(), + (proj * Matrix4::look_to_rh(position.into(), Into::>::into(position) + Vector3::new( 0.0,-1.0, 0.0), Vector3::new(0.0, 0.0,-1.0))).into(), + (proj * Matrix4::look_to_rh(position.into(), Into::>::into(position) + Vector3::new( 0.0, 0.0, 1.0), Vector3::new(0.0,-1.0, 0.0))).into(), + (proj * Matrix4::look_to_rh(position.into(), Into::>::into(position) + Vector3::new( 0.0, 0.0,-1.0), Vector3::new(0.0,-1.0, 0.0))).into(), + ]; + Self { position, _padding: 0, color, + matrices: matrices, + active_matrix: 0, + _padding2: [0, 0, 0] } } } diff --git a/src/core/state.rs b/src/core/state.rs index 27e09e9..24717f8 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -1,4 +1,5 @@ use cgmath::prelude::*; +use std::num::NonZeroU32; use std::time::Duration; use wgpu::util::DeviceExt; @@ -35,7 +36,7 @@ pub struct State { light_debug_pass: RenderPass, light_bind_group: wgpu::BindGroup, light_depth_pass: RenderPass, - light_depth_texture: Texture, + light_depth_textures: [Texture; 6], } impl State { @@ -57,7 +58,8 @@ impl State { let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { - features: wgpu::Features::empty(), + features: wgpu::Features::TEXTURE_BINDING_ARRAY + | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, limits: if cfg!(target_arch = "wasm32") { wgpu::Limits::downlevel_webgl2_defaults() } else { @@ -128,13 +130,34 @@ impl State { &config, "depth_texture", Some(wgpu::CompareFunction::LessEqual), + 1, ); - let light_depth_texture = Texture::create_depth_texture( - &device, - &config, - "light_depth_texture", - Some(wgpu::CompareFunction::LessEqual), - ); + + let light_depth_textures: [Texture; 6] = (0..6) + .map(|i| { + Texture::create_depth_texture( + &device, + &config, + format!("light_depth_texture_{}", i).as_str(), + Some(wgpu::CompareFunction::LessEqual), + 1, + ) + }) + .collect::>() + .try_into() + .expect("failed to create light depth textures"); + + let light_depth_texture_views: [&wgpu::TextureView; 6] = (0..6) + .map(|i| &light_depth_textures[i].view) + .collect::>() + .try_into() + .expect("failed to create light depth texture views"); + + let light_depth_texture_samplers: [&wgpu::Sampler; 6] = (0..6) + .map(|i| &light_depth_textures[i].sampler) + .collect::>() + .try_into() + .expect("failed to create light depth texture samplers"); let light_uniform = LightUniform::new([100.0, 60.0, 0.0], [1.0, 1.0, 1.0, 200000.0]); @@ -148,6 +171,7 @@ impl State { let light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ + // LightUniform wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, @@ -167,13 +191,13 @@ impl State { view_dimension: wgpu::TextureViewDimension::D2, sample_type: wgpu::TextureSampleType::Depth, }, - count: None, + count: NonZeroU32::new(6), }, wgpu::BindGroupLayoutEntry { binding: 2, visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison), - count: None, + count: NonZeroU32::new(6), }, ], label: Some("Light Bind Group Layout"), @@ -188,11 +212,11 @@ impl State { }, wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::TextureView(&light_depth_texture.view), + resource: wgpu::BindingResource::TextureViewArray(&light_depth_texture_views), }, wgpu::BindGroupEntry { binding: 2, - resource: wgpu::BindingResource::Sampler(&light_depth_texture.sampler), + resource: wgpu::BindingResource::SamplerArray(&light_depth_texture_samplers), }, ], label: Some("Light Bind Group"), @@ -351,7 +375,7 @@ impl State { light_debug_pass, light_bind_group, light_depth_pass, - light_depth_texture, + light_depth_textures, } } @@ -369,13 +393,17 @@ impl State { &self.config, "depth_texture", Some(wgpu::CompareFunction::LessEqual), + 1, ); - self.light_depth_texture = Texture::create_depth_texture( - &self.device, - &self.config, - "light_depth_texture", - Some(wgpu::CompareFunction::LessEqual), - ); + for i in 0..6 { + self.light_depth_textures[i] = Texture::create_depth_texture( + &self.device, + &self.config, + format!("light_depth_texture_{}", i).as_str(), + Some(wgpu::CompareFunction::LessEqual), + 1, + ); + } } } @@ -422,14 +450,20 @@ impl State { label: Some("Render Encoder"), }); - /* - { + for i in 0..6 { + self.light_uniform.active_matrix = i as u32; + self.queue.write_buffer( + &self.light_buffer, + 0, + bytemuck::cast_slice(&[self.light_uniform]), + ); + 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, + view: &self.light_depth_textures[i].view, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: true, @@ -440,14 +474,14 @@ impl State { light_depth_render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); light_depth_render_pass.set_pipeline(&self.light_depth_pass.pipeline); + // TODO separate func light_depth_render_pass.draw_model_instanced( - &self.model, + &self.light_model, 0..self.instances.len() as u32, - &self.camera_bind_group, // TODO light projection - &self.light_bind_group, // TODO remove + &self.camera_bind_group, + &self.light_bind_group, ); } - */ { let mut geom_render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { diff --git a/src/core/texture.rs b/src/core/texture.rs index 423a3cc..79fcacd 100644 --- a/src/core/texture.rs +++ b/src/core/texture.rs @@ -1,5 +1,6 @@ use anyhow::*; +#[derive(Debug)] pub struct Texture { pub texture: wgpu::Texture, pub view: wgpu::TextureView, @@ -14,11 +15,12 @@ impl Texture { config: &wgpu::SurfaceConfiguration, label: &str, compare: Option, + layers: u32, ) -> Self { let size = wgpu::Extent3d { width: config.width, height: config.height, - depth_or_array_layers: 1, + depth_or_array_layers: layers, }; let desc = wgpu::TextureDescriptor { label: Some(label),