feat(renderer): add RT shadow compute pipeline and integrate into lighting pass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,8 @@ struct ShadowUniform {
|
||||
@group(2) @binding(4) var s_brdf_lut: sampler;
|
||||
@group(2) @binding(5) var t_ssgi: texture_2d<f32>;
|
||||
@group(2) @binding(6) var s_ssgi: sampler;
|
||||
@group(2) @binding(7) var t_rt_shadow: texture_2d<f32>;
|
||||
@group(2) @binding(8) var s_rt_shadow: sampler;
|
||||
|
||||
// ── Vertex / Fragment structs ─────────────────────────────────────────────────
|
||||
|
||||
@@ -264,8 +266,8 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
// F0: base reflectivity; 0.04 for dielectrics, albedo for metals
|
||||
let F0 = mix(vec3<f32>(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<f32>(0.0);
|
||||
|
||||
@@ -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::<RtShadowUniform>() 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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user