chore(world): add temporary shadow pipeline diagnostics
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled

Shadows still go missing intermittently despite the per-frame
needsUpdate fix. Add temporary console logs to narrow down the cause:

- [shadow:mount]: one-shot snapshot of renderer shadow flags and a
  scene.traverse count of meshes with castShadow / receiveShadow at
  Lighting mount time.
- [shadow:tick]: every 2s during useFrame, log shadow map enabled flag,
  autoUpdate, sun.castShadow, sun intensity, shadow map texture
  presence, sun and target world positions, and renderer draw calls.

To be removed once the root cause is identified.
This commit is contained in:
Tom Boullay
2026-06-01 16:50:21 +02:00
parent da7d66e1fd
commit a2a491bd5c
+54 -2
View File
@@ -1,10 +1,12 @@
import { useEffect, useRef } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import {
Mesh,
PCFShadowMap,
type AmbientLight,
type DirectionalLight,
type Object3D,
type Scene,
type WebGLRenderer,
} from "three";
import {
@@ -51,12 +53,33 @@ function configureSunShadow(sun: DirectionalLight, sunTarget: Object3D): void {
sun.shadow.camera.updateProjectionMatrix();
}
// [diag] temporary helper: count shadow-casting/receiving meshes in the scene
function snapshotShadowMeshes(scene: Scene): {
meshCount: number;
castShadowCount: number;
receiveShadowCount: number;
} {
let meshCount = 0;
let castShadowCount = 0;
let receiveShadowCount = 0;
scene.traverse((obj) => {
if (obj instanceof Mesh) {
meshCount += 1;
if (obj.castShadow) castShadowCount += 1;
if (obj.receiveShadow) receiveShadowCount += 1;
}
});
return { meshCount, castShadowCount, receiveShadowCount };
}
export function Lighting(): React.JSX.Element {
const camera = useThree((state) => state.camera);
const gl = useThree((state) => state.gl);
const scene = useThree((state) => state.scene);
const ambient = useRef<AmbientLight>(null);
const sun = useRef<DirectionalLight>(null);
const sunTarget = useRef<Object3D>(null);
const lastDiagAtRef = useRef(0);
useEffect(() => {
if (!sun.current || !sunTarget.current) return;
@@ -64,7 +87,18 @@ export function Lighting(): React.JSX.Element {
configureSunShadow(sun.current, sunTarget.current);
configureRendererShadows(gl);
sun.current.shadow.needsUpdate = true;
}, [gl]);
// [diag] one-shot scene snapshot to count shadow casters/receivers
const counts = snapshotShadowMeshes(scene);
console.log("[shadow:mount]", {
shadowMapEnabled: gl.shadowMap.enabled,
shadowMapType: gl.shadowMap.type,
shadowAutoUpdate: gl.shadowMap.autoUpdate,
sunCastShadow: sun.current.castShadow,
hasShadowMap: !!sun.current.shadow.map,
...counts,
});
}, [gl, scene]);
useDebugFolder("Lighting", (folder) => {
folder.addColor(LIGHTING_STATE, "ambientColor").name("Ambient Color");
@@ -98,7 +132,7 @@ export function Lighting(): React.JSX.Element {
.name("Sun Z");
});
useFrame(() => {
useFrame(({ clock }) => {
if (ambient.current) {
ambient.current.color.set(LIGHTING_STATE.ambientColor);
ambient.current.intensity = LIGHTING_STATE.ambientIntensity;
@@ -117,6 +151,24 @@ export function Lighting(): React.JSX.Element {
sun.current.updateMatrixWorld();
sun.current.shadow.needsUpdate = true;
}
// [diag] periodic shadow pipeline check (every 2s)
const now = clock.getElapsedTime();
if (now - lastDiagAtRef.current > 2 && sun.current) {
lastDiagAtRef.current = now;
console.log("[shadow:tick]", {
shadowMapEnabled: gl.shadowMap.enabled,
shadowAutoUpdate: gl.shadowMap.autoUpdate,
sunCastShadow: sun.current.castShadow,
sunIntensity: sun.current.intensity,
hasShadowMapTexture: !!sun.current.shadow.map?.texture,
sunPos: sun.current.position.toArray().map((n) => Number(n.toFixed(2))),
targetPos: sunTarget.current?.position
.toArray()
.map((n) => Number(n.toFixed(2))),
renderCalls: gl.info.render.calls,
});
}
});
return (