fix(renderer): fix RT shadow shader syntax, experimental features, and R32Float sampler compatibility
- Add 'enable wgpu_ray_query' to RT shadow shader - Fix RayDesc struct constructor syntax and rayQueryGetCommittedIntersection API - Enable ExperimentalFeatures in GpuContext for RT - Change RT shadow texture binding to non-filterable (R32Float) - Use textureLoad instead of textureSample for RT shadow in lighting pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -267,7 +267,9 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let F0 = mix(vec3<f32>(0.04, 0.04, 0.04), albedo, metallic);
|
||||
|
||||
// Shadow: sample RT shadow texture (1.0 = lit, 0.0 = shadowed)
|
||||
let shadow_factor = textureSample(t_rt_shadow, s_rt_shadow, uv).r;
|
||||
let rt_dims = textureDimensions(t_rt_shadow);
|
||||
let rt_coord = vec2<i32>(vec2<f32>(uv.x * f32(rt_dims.x), uv.y * f32(rt_dims.y)));
|
||||
let shadow_factor = textureLoad(t_rt_shadow, rt_coord, 0).r;
|
||||
|
||||
// Accumulate contribution from all active lights
|
||||
var Lo = vec3<f32>(0.0);
|
||||
|
||||
@@ -274,22 +274,22 @@ 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)
|
||||
// binding 7: RT shadow texture (R32Float → non-filterable)
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 7,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
// binding 8: filtering sampler for RT shadow texture
|
||||
// binding 8: non-filtering sampler for RT shadow texture
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 8,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -63,12 +63,20 @@ impl GpuContext {
|
||||
adapter.limits()
|
||||
};
|
||||
|
||||
let experimental = if extra_features.is_empty() {
|
||||
wgpu::ExperimentalFeatures::disabled()
|
||||
} else {
|
||||
// Safety: we acknowledge experimental features may have bugs
|
||||
unsafe { wgpu::ExperimentalFeatures::enabled() }
|
||||
};
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
label: Some("Voltex Device"),
|
||||
required_features: extra_features,
|
||||
required_limits,
|
||||
memory_hints: Default::default(),
|
||||
experimental_features: experimental,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
enable wgpu_ray_query;
|
||||
|
||||
// RT Shadow compute shader.
|
||||
// Reads world-space position and normal from the G-Buffer, then fires a
|
||||
// shadow ray against the TLAS to determine per-pixel visibility.
|
||||
@@ -51,27 +53,24 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let ray_origin = world_pos + N * 0.01;
|
||||
let ray_dir = normalize(-uniforms.light_direction);
|
||||
|
||||
// Ray query: check for any occluder between surface and "infinity"
|
||||
var rq: ray_query;
|
||||
rayQueryInitialize(
|
||||
&rq,
|
||||
tlas,
|
||||
RayDesc(
|
||||
RAY_FLAG_TERMINATE_ON_FIRST_HIT | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER,
|
||||
0xFF,
|
||||
0.0001,
|
||||
1.0e6,
|
||||
ray_origin,
|
||||
ray_dir,
|
||||
),
|
||||
);
|
||||
// Build ray descriptor
|
||||
var desc: RayDesc;
|
||||
desc.flags = RAY_FLAG_TERMINATE_ON_FIRST_HIT;
|
||||
desc.cull_mask = 0xFFu;
|
||||
desc.tmin = 0.001;
|
||||
desc.tmax = 1000.0;
|
||||
desc.origin = ray_origin;
|
||||
desc.dir = ray_dir;
|
||||
|
||||
// Advance until the query is done (with TERMINATE_ON_FIRST_HIT this is at most one step)
|
||||
// Ray query
|
||||
var rq: ray_query;
|
||||
rayQueryInitialize(&rq, tlas, desc);
|
||||
while rayQueryProceed(&rq) {}
|
||||
|
||||
// If anything was hit, the pixel is in shadow
|
||||
// Check result
|
||||
let intersection = rayQueryGetCommittedIntersection(&rq);
|
||||
var shadow: f32 = 1.0;
|
||||
if rayQueryGetCommittedIntersectionType(&rq) != RAY_QUERY_INTERSECTION_NONE {
|
||||
if intersection.kind != RAY_QUERY_INTERSECTION_NONE {
|
||||
shadow = 0.0;
|
||||
}
|
||||
|
||||
|
||||
@@ -427,13 +427,13 @@ impl ApplicationHandler for DeferredDemoApp {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let rt_shadow_filtering_sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("RT Shadow Filtering Sampler"),
|
||||
let rt_shadow_nearest_sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("RT Shadow Nearest Sampler"),
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||
..Default::default()
|
||||
});
|
||||
@@ -447,7 +447,7 @@ impl ApplicationHandler for DeferredDemoApp {
|
||||
&ssgi,
|
||||
&ssgi_filtering_sampler,
|
||||
&rt_shadow,
|
||||
&rt_shadow_filtering_sampler,
|
||||
&rt_shadow_nearest_sampler,
|
||||
);
|
||||
|
||||
// Lighting pipeline — now renders to HDR format instead of surface
|
||||
@@ -696,13 +696,13 @@ impl ApplicationHandler for DeferredDemoApp {
|
||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||
..Default::default()
|
||||
});
|
||||
let rt_shadow_filtering_sampler = state.gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("RT Shadow Filtering Sampler"),
|
||||
let rt_shadow_nearest_sampler = state.gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("RT Shadow Nearest Sampler"),
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||
..Default::default()
|
||||
});
|
||||
@@ -715,7 +715,7 @@ impl ApplicationHandler for DeferredDemoApp {
|
||||
&state.ssgi,
|
||||
&ssgi_filtering_sampler,
|
||||
&state.rt_shadow,
|
||||
&rt_shadow_filtering_sampler,
|
||||
&rt_shadow_nearest_sampler,
|
||||
);
|
||||
|
||||
// Resize HDR target
|
||||
|
||||
Reference in New Issue
Block a user