diff --git a/crates/voltex_renderer/src/deferred_lighting.wgsl b/crates/voltex_renderer/src/deferred_lighting.wgsl index 6553e8d..602fb2e 100644 --- a/crates/voltex_renderer/src/deferred_lighting.wgsl +++ b/crates/voltex_renderer/src/deferred_lighting.wgsl @@ -51,6 +51,8 @@ struct ShadowUniform { @group(2) @binding(4) var s_brdf_lut: sampler; @group(2) @binding(5) var t_ssgi: texture_2d; @group(2) @binding(6) var s_ssgi: sampler; +@group(2) @binding(7) var t_rt_shadow: texture_2d; +@group(2) @binding(8) var s_rt_shadow: sampler; // ── Vertex / Fragment structs ───────────────────────────────────────────────── @@ -264,8 +266,8 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { // F0: base reflectivity; 0.04 for dielectrics, albedo for metals let F0 = mix(vec3(0.04, 0.04, 0.04), albedo, metallic); - // Shadow - let shadow_factor = calculate_shadow(world_pos); + // Shadow: sample RT shadow texture (1.0 = lit, 0.0 = shadowed) + let shadow_factor = textureSample(t_rt_shadow, s_rt_shadow, uv).r; // Accumulate contribution from all active lights var Lo = vec3(0.0); diff --git a/crates/voltex_renderer/src/deferred_pipeline.rs b/crates/voltex_renderer/src/deferred_pipeline.rs index 34b0d02..c04cc14 100644 --- a/crates/voltex_renderer/src/deferred_pipeline.rs +++ b/crates/voltex_renderer/src/deferred_pipeline.rs @@ -6,6 +6,7 @@ use crate::gbuffer::{ }; use crate::light::CameraUniform; use crate::ssgi::{SsgiUniform, SSGI_OUTPUT_FORMAT}; +use crate::rt_shadow::{RtShadowUniform, RT_SHADOW_FORMAT}; /// Bind group layout for the G-Buffer pass camera uniform (dynamic offset, group 0). pub fn gbuffer_camera_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { @@ -270,6 +271,24 @@ pub fn lighting_shadow_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGro ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, + // binding 7: RT shadow texture (R32Float → filterable float) + wgpu::BindGroupLayoutEntry { + binding: 7, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + // binding 8: filtering sampler for RT shadow texture + wgpu::BindGroupLayoutEntry { + binding: 8, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, ], }) } @@ -495,3 +514,105 @@ pub fn create_ssgi_pipeline( cache: None, }) } + +// ── RT Shadow pipeline ──────────────────────────────────────────────────────── + +/// Bind group layout for the RT shadow compute pass G-Buffer inputs (group 0). +/// Bindings: position (non-filterable), normal (filterable) — both COMPUTE visibility. +pub fn rt_shadow_gbuffer_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("RT Shadow GBuffer Bind Group Layout"), + entries: &[ + // binding 0: position texture (Rgba32Float → non-filterable) + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + // binding 1: normal texture (filterable) + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + ], + }) +} + +/// Bind group layout for the RT shadow compute pass data (group 1). +/// Bindings: TLAS, storage texture (R32Float write), RtShadowUniform — all COMPUTE visibility. +pub fn rt_shadow_data_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("RT Shadow Data Bind Group Layout"), + entries: &[ + // binding 0: TLAS (acceleration structure) + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::AccelerationStructure { vertex_return: false }, + count: None, + }, + // binding 1: shadow output texture (R32Float, write-only storage) + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: RT_SHADOW_FORMAT, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + // binding 2: RtShadowUniform + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new( + std::mem::size_of::() as u64, + ), + }, + count: None, + }, + ], + }) +} + +/// Create the RT shadow compute pipeline. +pub fn create_rt_shadow_pipeline( + device: &wgpu::Device, + gbuffer_layout: &wgpu::BindGroupLayout, + data_layout: &wgpu::BindGroupLayout, +) -> wgpu::ComputePipeline { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("RT Shadow Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("rt_shadow_shader.wgsl").into()), + }); + + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("RT Shadow Pipeline Layout"), + bind_group_layouts: &[gbuffer_layout, data_layout], + immediate_size: 0, + }); + + device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("RT Shadow Compute Pipeline"), + layout: Some(&layout), + module: &shader, + entry_point: Some("cs_main"), + compilation_options: wgpu::PipelineCompilationOptions::default(), + cache: None, + }) +} diff --git a/crates/voltex_renderer/src/lib.rs b/crates/voltex_renderer/src/lib.rs index 556a712..bc75293 100644 --- a/crates/voltex_renderer/src/lib.rs +++ b/crates/voltex_renderer/src/lib.rs @@ -17,6 +17,8 @@ pub mod gbuffer; pub mod fullscreen_quad; pub mod deferred_pipeline; pub mod ssgi; +pub mod rt_accel; +pub mod rt_shadow; pub use gpu::{GpuContext, DEPTH_FORMAT}; pub use light::{CameraUniform, LightUniform, LightData, LightsUniform, MAX_LIGHTS, LIGHT_DIRECTIONAL, LIGHT_POINT, LIGHT_SPOT}; @@ -36,5 +38,8 @@ pub use deferred_pipeline::{ gbuffer_camera_bind_group_layout, lighting_gbuffer_bind_group_layout, lighting_lights_bind_group_layout, lighting_shadow_bind_group_layout, ssgi_gbuffer_bind_group_layout, ssgi_data_bind_group_layout, create_ssgi_pipeline, + rt_shadow_gbuffer_bind_group_layout, rt_shadow_data_bind_group_layout, create_rt_shadow_pipeline, }; pub use ssgi::{SsgiResources, SsgiUniform, SSGI_OUTPUT_FORMAT}; +pub use rt_accel::{RtAccel, BlasMeshData, mat4_to_tlas_transform}; +pub use rt_shadow::{RtShadowResources, RtShadowUniform, RT_SHADOW_FORMAT};