viewer: center shadow frustum on focus so it tracks the view (#347)
The directional shadow light was placed at focus + theme offset (only ~17-32 units), while the ortho shadow camera ran near=1/far=100. That left the focus near the front of a long frustum whose far end swung around as the look-at moved, so shadows appeared not to follow the view. Park the light at a fixed distance along its (preserved) direction and bracket near/far around that distance so the focus stays centered in the frustum depth. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
b8d94a6436
commit
37a3aba672
@@ -26,6 +26,16 @@ const SHADOWS_DISABLED =
|
|||||||
// deliberate middle ground — present, but not the heavy contact shadow there.
|
// deliberate middle ground — present, but not the heavy contact shadow there.
|
||||||
const MAX_SHADOW_INTENSITY = 0.55
|
const MAX_SHADOW_INTENSITY = 0.55
|
||||||
|
|
||||||
|
// Shadow frustum framing. The directional light is parked at a fixed distance
|
||||||
|
// along its (theme-defined) direction from the focus point, and the ortho
|
||||||
|
// shadow camera's depth is centred on that focus. Theme offsets are only
|
||||||
|
// ~17–32 units long, so without a fixed distance the focus sits near the front
|
||||||
|
// of a long frustum and the far end swings around as the focus moves. Keeping
|
||||||
|
// the light far away and the focus centred keeps the frustum hugging the view.
|
||||||
|
const SHADOW_DISTANCE = 120
|
||||||
|
const SHADOW_NEAR = 20
|
||||||
|
const SHADOW_FAR = 220
|
||||||
|
|
||||||
export function Lights() {
|
export function Lights() {
|
||||||
const sceneTheme = useViewer((state) => state.sceneTheme)
|
const sceneTheme = useViewer((state) => state.sceneTheme)
|
||||||
const theme = getSceneTheme(sceneTheme)
|
const theme = getSceneTheme(sceneTheme)
|
||||||
@@ -39,6 +49,8 @@ export function Lights() {
|
|||||||
// ortho shadow camera only covers ±shadowCameraSize around the light target,
|
// ortho shadow camera only covers ±shadowCameraSize around the light target,
|
||||||
// so it has to track the view or anything far from origin gets no shadows.
|
// so it has to track the view or anything far from origin gets no shadows.
|
||||||
const shadowFocus = useRef(new THREE.Vector3())
|
const shadowFocus = useRef(new THREE.Vector3())
|
||||||
|
// Scratch vector for the per-light direction, reused to avoid per-frame allocs.
|
||||||
|
const shadowDir = useRef(new THREE.Vector3())
|
||||||
|
|
||||||
const hemiRef = useRef<HemisphereLight>(null)
|
const hemiRef = useRef<HemisphereLight>(null)
|
||||||
const ambientRef = useRef<AmbientLight>(null)
|
const ambientRef = useRef<AmbientLight>(null)
|
||||||
@@ -77,7 +89,13 @@ export function Lights() {
|
|||||||
const light = lightRefs.current[index]
|
const light = lightRefs.current[index]
|
||||||
if (!(config?.castShadow && light)) continue
|
if (!(config?.castShadow && light)) continue
|
||||||
const [ox, oy, oz] = config.position
|
const [ox, oy, oz] = config.position
|
||||||
light.position.set(focus.x + ox, focus.y + oy, focus.z + oz)
|
// Preserve the theme's light DIRECTION but park the light at a fixed
|
||||||
|
// distance, so the ortho frustum (depth centred via SHADOW_NEAR/FAR)
|
||||||
|
// stays centred on the focus instead of dangling far past it.
|
||||||
|
const dir = shadowDir.current.set(ox, oy, oz)
|
||||||
|
if (dir.lengthSq() === 0) dir.set(0, 1, 0)
|
||||||
|
dir.normalize().multiplyScalar(SHADOW_DISTANCE)
|
||||||
|
light.position.set(focus.x + dir.x, focus.y + dir.y, focus.z + dir.z)
|
||||||
light.target.position.copy(focus)
|
light.target.position.copy(focus)
|
||||||
light.target.updateMatrixWorld()
|
light.target.updateMatrixWorld()
|
||||||
}
|
}
|
||||||
@@ -175,9 +193,9 @@ export function Lights() {
|
|||||||
<orthographicCamera
|
<orthographicCamera
|
||||||
attach="shadow-camera"
|
attach="shadow-camera"
|
||||||
bottom={-shadowCameraSize}
|
bottom={-shadowCameraSize}
|
||||||
far={100}
|
far={SHADOW_FAR}
|
||||||
left={-shadowCameraSize}
|
left={-shadowCameraSize}
|
||||||
near={1}
|
near={SHADOW_NEAR}
|
||||||
ref={shadowCamera}
|
ref={shadowCamera}
|
||||||
right={shadowCameraSize}
|
right={shadowCameraSize}
|
||||||
top={shadowCameraSize}
|
top={shadowCameraSize}
|
||||||
|
|||||||
Reference in New Issue
Block a user