From ccaf9261cc156b1ef782317f4272d447cf1b1c0f Mon Sep 17 00:00:00 2001 From: nullprop Date: Sun, 5 Nov 2023 02:27:38 +0200 Subject: [PATCH] Disable shadowmapping on WebGL --- README.md | 9 +++++--- index.html | 18 ++++++++++----- res/shaders/depth.wgsl | 2 +- res/shaders/globals.wgsl | 8 +++---- res/shaders/pbr.wgsl | 47 +++++++++++++++++++++++----------------- src/core/light.rs | 7 +++--- src/core/state.rs | 21 ++++++++++-------- 7 files changed, 67 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index da77296..53fccc4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ A small [wgpu](https://github.com/gfx-rs/wgpu) renderer written in [Rust](https: ## Try it out [rasanen.dev/wgpu-renderer](https://rasanen.dev/wgpu-renderer) -Note: the .wasm is about 50 MB because it embeds Sponza. +Note: the .wasm is about 50 MB because it embeds Sponza. +Note: shadowmaps are not enabled on WebGL. Controls: - WASD - Move horizontally @@ -18,8 +19,8 @@ Controls: - PBS - glTF models - 1 realtime pointlight -- Shadow mapping -- Soft shadows +- Shadow mapping* +- Soft shadows* - Simple wgsl preprocessor for includes - Runs on WASM and native desktop - Tested on: @@ -28,6 +29,8 @@ Controls: - `Firefox 109.0` - `Chrome 109.0.5414.120` +*Native only + TODO: - Transparency - Restructuring diff --git a/index.html b/index.html index cff5a73..3cd5676 100644 --- a/index.html +++ b/index.html @@ -12,12 +12,14 @@ padding: 0; background: black; } + canvas { background-color: black; display: block; width: 100vw; height: 100vh; } + #loading { color: white; position: absolute; @@ -29,14 +31,20 @@ -

Loading...

- + // } + \ No newline at end of file diff --git a/res/shaders/depth.wgsl b/res/shaders/depth.wgsl index ea80675..90676e5 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 light.matrices[light_matrix_index.value] * world_position; + return light.matrices[global_uniforms.light_matrix_index] * world_position; } diff --git a/res/shaders/globals.wgsl b/res/shaders/globals.wgsl index 77b3757..ed1bd96 100644 --- a/res/shaders/globals.wgsl +++ b/res/shaders/globals.wgsl @@ -17,14 +17,14 @@ struct Light { var light: Light; // Needs to be 16 bytes in WebGL -struct LightMatrixIndex { - value: u32, - _padding: u32, +struct GlobalUniforms { + light_matrix_index: u32, + use_shadowmaps: u32, _padding1: u32, _padding2: u32, } @group(1) @binding(1) -var light_matrix_index: LightMatrixIndex; +var global_uniforms: GlobalUniforms; struct VertexInput { @location(0) position: vec3, diff --git a/res/shaders/pbr.wgsl b/res/shaders/pbr.wgsl index 9ae9de4..bfd5114 100644 --- a/res/shaders/pbr.wgsl +++ b/res/shaders/pbr.wgsl @@ -116,29 +116,36 @@ fn fs_main(vert: VertexOutput) -> @location(0) vec4 { var total_radiance: vec3; var in_light = 0.0; - for (var i: i32 = 0; i < 6; i++) { - let light_coords = light.matrices[i] * vert.world_position; - let light_dir = normalize(light_coords.xyz); - let bias = 0.01; - // z can never be smaller than this inside 90 degree frustum - if (light_dir.z < INV_SQRT_3 - bias) { - continue; - } - // x and y can never be larger than this inside frustum - if (abs(light_dir.y) > INV_SQRT_2 + bias) { - continue; - } - if (abs(light_dir.x) > INV_SQRT_2 + bias) { - continue; - } + // Depth sampling is broken in WebGL... + // TODO: remove once WebGPU + if (global_uniforms.use_shadowmaps > 0u) { + for (var i: i32 = 0; i < 6; i++) { + let light_coords = light.matrices[i] * vert.world_position; - in_light = sample_direct_light(i, light_coords); - // TODO should break even if 0 since we're inside frustum. - // See if causes issues with bias overlap between directions. - if (in_light > 0.0) { - break; + let light_dir = normalize(light_coords.xyz); + let bias = 0.01; + // z can never be smaller than this inside 90 degree frustum + if (light_dir.z < INV_SQRT_3 - bias) { + continue; + } + // x and y can never be larger than this inside frustum + if (abs(light_dir.y) > INV_SQRT_2 + bias) { + continue; + } + if (abs(light_dir.x) > INV_SQRT_2 + bias) { + continue; + } + + in_light = sample_direct_light(i, light_coords); + // TODO should break even if 0 since we're inside frustum. + // See if causes issues with bias overlap between directions. + if (in_light > 0.0) { + break; + } } + } else { + in_light = 1.0; } if (in_light > 0.0) { diff --git a/src/core/light.rs b/src/core/light.rs index a769be3..e763a76 100644 --- a/src/core/light.rs +++ b/src/core/light.rs @@ -43,10 +43,11 @@ impl LightUniform { #[repr(C)] #[derive(Debug, Default, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -pub struct LightMatrixUniform { - pub value: u32, +pub struct GlobalUniforms { + pub light_matrix_index: u32, // No DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED in WebGL - _padding: [u32; 3], + pub use_shadowmaps: u32, + _padding: [u32; 2], } pub trait DrawLight<'a> { diff --git a/src/core/state.rs b/src/core/state.rs index 3a4e6c3..ced2ffb 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -7,7 +7,7 @@ use std::time::Duration; use wgpu::util::DeviceExt; use winit::{event::*, window::Window}; use winit::dpi::PhysicalSize; -use crate::core::light::LightMatrixUniform; +use crate::core::light::GlobalUniforms; use super::camera::{Camera, CameraController, CameraUniform}; use super::instance::{Instance, InstanceRaw}; @@ -45,7 +45,7 @@ pub struct State { light_depth_bind_group: wgpu::BindGroup, light_depth_pass: RenderPass, light_depth_texture_target_views: [TextureView; SHADOW_MAP_LAYERS as usize], - light_matrix_uniform: LightMatrixUniform, + light_matrix_uniform: GlobalUniforms, light_matrix_buffer: wgpu::Buffer, } @@ -56,7 +56,7 @@ impl State { let mut size = window.inner_size(); size.width = size.width.max(1); size.height = size.height.max(1); - let instance = wgpu::Instance::new(InstanceDescriptor { backends: Backends::all(), ..Default::default() }); + let instance = wgpu::Instance::new(InstanceDescriptor { backends: Backends::PRIMARY | Backends::GL, ..Default::default() }); let surface = unsafe { instance.create_surface(window) }.unwrap(); let adapter = instance @@ -73,13 +73,14 @@ impl State { &wgpu::DeviceDescriptor { features: wgpu::Features::default(), limits: if cfg!(target_arch = "wasm32") { + // TODO: remove once webgpu? wgpu::Limits::downlevel_webgl2_defaults() } else { wgpu::Limits::default() }, label: None, }, - None, // Trace path + None, ) .await .expect("failed to get device"); @@ -186,7 +187,7 @@ impl State { usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, }); - let light_matrix_uniform = LightMatrixUniform::default(); + let light_matrix_uniform = GlobalUniforms::default(); let light_matrix_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Light Matrix UB"), contents: bytemuck::cast_slice(&[light_matrix_uniform]), @@ -194,7 +195,7 @@ impl State { }); let light_uniform_size = mem::size_of::() as u64; - let light_matrix_uniform_size = mem::size_of::() as u64; + let light_matrix_uniform_size = mem::size_of::() as u64; let light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[ @@ -209,10 +210,11 @@ impl State { }, count: None, }, - // matrix index + // matrix index, use shadowmaps wgpu::BindGroupLayoutEntry { binding: 1, - visibility: wgpu::ShaderStages::VERTEX, + // TODO: remove fragment vis once no shadowmap uniform + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, @@ -506,7 +508,8 @@ impl State { // render light to depth textures for i in 0..SHADOW_MAP_LAYERS as usize { - self.light_matrix_uniform.value = i as u32; + self.light_matrix_uniform.light_matrix_index = i as u32; + self.light_matrix_uniform.use_shadowmaps = if cfg!(target_arch = "wasm32") { 0u32 } else { 1u32 }; self.queue.write_buffer( &self.light_matrix_buffer, 0,