Fix shadow_test: WebGPU clip depth, correct NdotL, shadow compare
- Use glam's directx (WebGPU) projection module for both the camera perspective and the shadow orthographic: NDC clip depth is [0,1] as wgpu expects, instead of OpenGL's [-1,1] which clipped half the frustum and broke depth-space consistency with the shadow map. - Extend the shadow orthographic far plane to 2*r so the whole scene box (and the shadow cast behind it, toward the camera) is covered. - Switch the shadow comparison sampler to GreaterEqual so open sky is lit and surfaces behind a blocker are shadowed (previous LessEqual inverted the shadow, blackening the entire ground and making the cube float). - Use the surface->light direction (+position_dir) for the directional N*L term; the old negation darkened the cube top and lit the camera faces, producing the inverted-pyramid appearance. - Drop the now-redundant [0,1] depth remap in the main-pass shader.
This commit is contained in:
@@ -161,11 +161,14 @@ impl Renderer {
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||
// Comparison sampler : `textureSampleCompare` returns 1 when the sampled depth passes
|
||||
// this test against the reference, 0 otherwise (D5). LessEqual = lit when the fragment
|
||||
// is no farther from the light than the surface recorded in the shadow map (the map is
|
||||
// cleared to 1.0 = far, so open un-blocked texels pass and surfaces behind a blocker fail).
|
||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
// The shadow map uses WebGPU `[0,1]` clip depth: the light's orthographic
|
||||
// projection is built with glam's `directx` (WebGPU) module so NDC z is already in
|
||||
// [0,1] and matches the `current_depth` computed in the main-pass shader. The
|
||||
// comparison sampler compares the recorded depth against the reference: a surface is
|
||||
// LIT when it is no farther from the light than the depth recorded in the map, i.e.
|
||||
// `stored_depth >= reference` (GreaterEqual). The map is cleared to 1.0 (far), so
|
||||
// un-blocked texels pass and surfaces behind a blocker fail.
|
||||
compare: Some(wgpu::CompareFunction::GreaterEqual),
|
||||
..Default::default()
|
||||
});
|
||||
let shadow_map_layout = create_shadow_map_bind_group_layout(&device);
|
||||
@@ -362,10 +365,14 @@ impl Renderer {
|
||||
// Avoid a degenerate basis when the light points straight down/up (parallel up vector).
|
||||
let up = if dir.y.abs() > 0.99 { Vec3::Z } else { Vec3::Y };
|
||||
let view = glam::camera::rh::view::look_at_mat4(eye, target, up);
|
||||
// Orthographic box of half-size r, near 0, far r (D1/D3), in the same OpenGL NDC convention
|
||||
// as the camera projection (wgpu maps NDC z ∈ [-1,1] to depth [0,1], see standard_shader).
|
||||
// Orthographic box of half-size r, near 0, far 2·r (D1/D3), in the WebGPU `[0,1]` NDC
|
||||
// convention (glam `directx` module), which matches the depth range wgpu writes to the
|
||||
// shadow map and the `current_depth` computed by the main-pass shader. The eye sits one
|
||||
// scene-radius behind the target, so the box [−r, r] around the target spans a depth range
|
||||
// of [0, 2r] from the eye: `far = 2·r` covers the whole box (and the shadows cast behind
|
||||
// it), whereas `far = r` would clip the far half.
|
||||
let proj =
|
||||
glam::camera::rh::proj::opengl::orthographic(-r, r, -r, r, 0.0, r);
|
||||
glam::camera::rh::proj::directx::orthographic(-r, r, -r, r, 0.0, 2.0 * r);
|
||||
Some((index, proj * view))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user