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:
Jérôme Bousquié
2026-09-19 16:10:47 +02:00
parent 67bd7af095
commit 9a51ff7602
3 changed files with 26 additions and 12 deletions
+6 -3
View File
@@ -162,9 +162,10 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
var diffuse = vec3<f32>(0.0);
// Lumières directionnelles (indices 0..num_directional). `position_dir` pointe de la surface
// vers la lumière ; on l'inverse pour le terme N·L.
// vers la lumière, donc on l'utilise tel quel pour le terme N·L (dot(n, direction vers la
// lumière) > 0 = face éclairée).
for (var i = 0u; i < frame.num_directional; i++) {
let l = normalize(-frame.lights[i].position_dir.xyz);
let l = normalize(frame.lights[i].position_dir.xyz);
let ndotl = max(dot(n, l), 0.0);
diffuse += frame.lights[i].color.rgb * frame.lights[i].color.a * ndotl;
}
@@ -220,7 +221,9 @@ fn compute_shadow(world_pos: vec3<f32>) -> f32 {
let shadow_ndc = light_clip.xyz / max(light_clip.w, 1e-6);
var shadow_uv = shadow_ndc.xy * 0.5 + 0.5;
shadow_uv = vec2<f32>(shadow_uv.x, 1.0 - shadow_uv.y); // flip V for texture coordinates
let current_depth = shadow_ndc.z * 0.5 + 0.5;
// The light projection is built with the WebGPU `[0,1]` clip-depth convention (glam
// directx/WebGPU module), so NDC z is already in [0,1]: no extra remap is needed.
let current_depth = shadow_ndc.z;
let bias = frame.shadow_params.y;
let texel = 1.0 / max(frame.shadow_params.x, 1.0);