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);
|
let F0 = mix(vec3<f32>(0.04, 0.04, 0.04), albedo, metallic);
|
||||||
|
|
||||||
// Shadow: sample RT shadow texture (1.0 = lit, 0.0 = shadowed)
|
// 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
|
// Accumulate contribution from all active lights
|
||||||
var Lo = vec3<f32>(0.0);
|
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),
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
// binding 7: RT shadow texture (R32Float → filterable float)
|
// binding 7: RT shadow texture (R32Float → non-filterable)
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 7,
|
binding: 7,
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
ty: wgpu::BindingType::Texture {
|
ty: wgpu::BindingType::Texture {
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
multisampled: false,
|
multisampled: false,
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
// binding 8: filtering sampler for RT shadow texture
|
// binding 8: non-filtering sampler for RT shadow texture
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 8,
|
binding: 8,
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -63,12 +63,20 @@ impl GpuContext {
|
|||||||
adapter.limits()
|
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
|
let (device, queue) = adapter
|
||||||
.request_device(&wgpu::DeviceDescriptor {
|
.request_device(&wgpu::DeviceDescriptor {
|
||||||
label: Some("Voltex Device"),
|
label: Some("Voltex Device"),
|
||||||
required_features: extra_features,
|
required_features: extra_features,
|
||||||
required_limits,
|
required_limits,
|
||||||
memory_hints: Default::default(),
|
memory_hints: Default::default(),
|
||||||
|
experimental_features: experimental,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
enable wgpu_ray_query;
|
||||||
|
|
||||||
// RT Shadow compute shader.
|
// RT Shadow compute shader.
|
||||||
// Reads world-space position and normal from the G-Buffer, then fires a
|
// Reads world-space position and normal from the G-Buffer, then fires a
|
||||||
// shadow ray against the TLAS to determine per-pixel visibility.
|
// 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_origin = world_pos + N * 0.01;
|
||||||
let ray_dir = normalize(-uniforms.light_direction);
|
let ray_dir = normalize(-uniforms.light_direction);
|
||||||
|
|
||||||
// Ray query: check for any occluder between surface and "infinity"
|
// Build ray descriptor
|
||||||
var rq: ray_query;
|
var desc: RayDesc;
|
||||||
rayQueryInitialize(
|
desc.flags = RAY_FLAG_TERMINATE_ON_FIRST_HIT;
|
||||||
&rq,
|
desc.cull_mask = 0xFFu;
|
||||||
tlas,
|
desc.tmin = 0.001;
|
||||||
RayDesc(
|
desc.tmax = 1000.0;
|
||||||
RAY_FLAG_TERMINATE_ON_FIRST_HIT | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER,
|
desc.origin = ray_origin;
|
||||||
0xFF,
|
desc.dir = ray_dir;
|
||||||
0.0001,
|
|
||||||
1.0e6,
|
|
||||||
ray_origin,
|
|
||||||
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) {}
|
while rayQueryProceed(&rq) {}
|
||||||
|
|
||||||
// If anything was hit, the pixel is in shadow
|
// Check result
|
||||||
|
let intersection = rayQueryGetCommittedIntersection(&rq);
|
||||||
var shadow: f32 = 1.0;
|
var shadow: f32 = 1.0;
|
||||||
if rayQueryGetCommittedIntersectionType(&rq) != RAY_QUERY_INTERSECTION_NONE {
|
if intersection.kind != RAY_QUERY_INTERSECTION_NONE {
|
||||||
shadow = 0.0;
|
shadow = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -427,13 +427,13 @@ impl ApplicationHandler for DeferredDemoApp {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let rt_shadow_filtering_sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
let rt_shadow_nearest_sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some("RT Shadow Filtering Sampler"),
|
label: Some("RT Shadow Nearest Sampler"),
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
mag_filter: wgpu::FilterMode::Nearest,
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
min_filter: wgpu::FilterMode::Nearest,
|
||||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@@ -447,7 +447,7 @@ impl ApplicationHandler for DeferredDemoApp {
|
|||||||
&ssgi,
|
&ssgi,
|
||||||
&ssgi_filtering_sampler,
|
&ssgi_filtering_sampler,
|
||||||
&rt_shadow,
|
&rt_shadow,
|
||||||
&rt_shadow_filtering_sampler,
|
&rt_shadow_nearest_sampler,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Lighting pipeline — now renders to HDR format instead of surface
|
// Lighting pipeline — now renders to HDR format instead of surface
|
||||||
@@ -696,13 +696,13 @@ impl ApplicationHandler for DeferredDemoApp {
|
|||||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let rt_shadow_filtering_sampler = state.gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
let rt_shadow_nearest_sampler = state.gpu.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some("RT Shadow Filtering Sampler"),
|
label: Some("RT Shadow Nearest Sampler"),
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
mag_filter: wgpu::FilterMode::Nearest,
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
min_filter: wgpu::FilterMode::Nearest,
|
||||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
@@ -715,7 +715,7 @@ impl ApplicationHandler for DeferredDemoApp {
|
|||||||
&state.ssgi,
|
&state.ssgi,
|
||||||
&ssgi_filtering_sampler,
|
&ssgi_filtering_sampler,
|
||||||
&state.rt_shadow,
|
&state.rt_shadow,
|
||||||
&rt_shadow_filtering_sampler,
|
&rt_shadow_nearest_sampler,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Resize HDR target
|
// Resize HDR target
|
||||||
|
|||||||
Reference in New Issue
Block a user