Skip to content

Commit 9cdb31c

Browse files
mvaligurskyMartin Valigursky
andauthored
Move frustum compute into Camera.updateFrustum (#8981)
Move the per-camera frustum computation from Renderer.updateCameraFrustum(camera) to a pure Camera.updateFrustum() - the frustum is camera state. Drop the cull-time matrix_viewInverse uniform write that lived in the old method (vestigial: only particle shaders read it, and they render after setCameraUniforms sets it). Update call sites: the culler, both shadow renderers, the lightmapper. Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
1 parent 619b3aa commit 9cdb31c

7 files changed

Lines changed: 43 additions & 33 deletions

File tree

src/framework/lightmapper/lightmapper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ class Lightmapper {
769769
shadowCam.aspectRatio = 1;
770770
shadowCam.fov = light._outerConeAngle * 2;
771771

772-
this.renderer.updateCameraFrustum(shadowCam);
772+
shadowCam.updateFrustum();
773773
}
774774
return shadowCam;
775775
}

src/scene/camera.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Vec4 } from '../core/math/vec4.js';
66
import { math } from '../core/math/math.js';
77
import { Frustum } from '../core/shape/frustum.js';
88
import {
9-
ASPECT_AUTO, PROJECTION_PERSPECTIVE, PROJECTION_ORTHOGRAPHIC,
9+
VIEW_CENTER, ASPECT_AUTO, PROJECTION_PERSPECTIVE, PROJECTION_ORTHOGRAPHIC,
1010
LAYERID_WORLD, LAYERID_DEPTH, LAYERID_SKYBOX, LAYERID_UI, LAYERID_IMMEDIATE
1111
} from './constants.js';
1212
import { FramePassColorGrab } from './graphics/frame-pass-color-grab.js';
@@ -30,6 +30,9 @@ const _point = new Vec3();
3030
const _invViewProjMat = new Mat4();
3131
const _xrViewProjMat = new Mat4();
3232
const _xrViewFrustum = new Frustum();
33+
const _frustumViewInvMat = new Mat4();
34+
const _frustumViewMat = new Mat4();
35+
const _frustumViewProjMat = new Mat4();
3336
const _frustumPoints = [new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3()];
3437

3538
/**
@@ -751,6 +754,39 @@ class Camera {
751754
return true;
752755
}
753756

757+
/**
758+
* Updates {@link Camera#frustum} for the camera's current transform and projection, for
759+
* visibility culling. Uses the combined (VIEW_CENTER) view; XR cameras delegate to
760+
* {@link Camera#updateXrFrustum}. Honors the {@link Camera#calculateProjection} and
761+
* {@link Camera#calculateTransform} overrides.
762+
*
763+
* @ignore
764+
*/
765+
updateFrustum() {
766+
767+
// XR: combined frustum from all views (avoids culling objects visible in only one eye)
768+
if (this.updateXrFrustum()) {
769+
return;
770+
}
771+
772+
const projMat = this.projectionMatrix;
773+
if (this.calculateProjection) {
774+
this.calculateProjection(projMat, VIEW_CENTER);
775+
}
776+
777+
if (this.calculateTransform) {
778+
this.calculateTransform(_frustumViewInvMat, VIEW_CENTER);
779+
} else {
780+
const pos = this._node.getPosition();
781+
const rot = this._node.getRotation();
782+
_frustumViewInvMat.setTRS(pos, rot, Vec3.ONE);
783+
}
784+
_frustumViewMat.copy(_frustumViewInvMat).invert();
785+
786+
_frustumViewProjMat.mul2(projMat, _frustumViewMat);
787+
this.frustum.setFromMat4(_frustumViewProjMat);
788+
}
789+
754790
/**
755791
* Convert a point from 3D world space to 2D canvas pixel space based on the camera's rect.
756792
*

src/scene/gsplat-unified/gsplat-projector-constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//
2020
// XR stereo variant (compiled with the `GSPLAT_XR` define; same 8 u32 / 32 B stride, so buffer
2121
// sizes are unchanged). Only the per-eye screen position is duplicated; everything else is shared
22-
// between the two eyes — valid because WebXR stereo is parallel-axis (see Renderer.updateCameraFrustum).
22+
// between the two eyes — valid because WebXR stereo is parallel-axis (see Camera.updateFrustum).
2323
// Perspective-only: clip.z is NOT stored, it is reconstructed in the hybrid VS from the shared w.
2424
// [0,1] ndc0.xy (f32) — eye 0 normalized device coords (clip.xy / clip.w)
2525
// [2,3] ndc1.xy (f32) — eye 1 normalized device coords

src/scene/renderer/culler.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ class Culler {
322322
const camera = comp.cameras[i];
323323

324324
// update the camera frustum for culling (aspect ratio auto-refreshes on read)
325-
renderer.updateCameraFrustum(camera.camera);
325+
camera.camera.updateFrustum();
326326

327327
// for all of its enabled layers cull the non-directional lights once with each camera
328328
// lights aren't collected anywhere, but marked as visible
@@ -421,7 +421,7 @@ class Culler {
421421
// precull is fired before the frustum is refreshed, so a listener may adjust the camera
422422
scene?.fire(EVENT_PRECULL, cameraComponent);
423423

424-
renderer.updateCameraFrustum(camera);
424+
camera.updateFrustum();
425425

426426
for (const layer of camera.cullLayers) {
427427
this.cullMeshInstances(camera, layer.meshInstances, layer.getCulledInstances(camera));

src/scene/renderer/renderer.js

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -502,32 +502,6 @@ class Renderer {
502502
this.setupCullModeAndFrontFace(cullFaces, flipFactor, drawCall);
503503
}
504504

505-
updateCameraFrustum(camera) {
506-
507-
// XR: combined frustum from all views (avoids culling objects visible in only one eye)
508-
if (camera.updateXrFrustum()) {
509-
return;
510-
}
511-
512-
const projMat = camera.projectionMatrix;
513-
if (camera.calculateProjection) {
514-
camera.calculateProjection(projMat, VIEW_CENTER);
515-
}
516-
517-
if (camera.calculateTransform) {
518-
camera.calculateTransform(viewInvMat, VIEW_CENTER);
519-
} else {
520-
const pos = camera._node.getPosition();
521-
const rot = camera._node.getRotation();
522-
viewInvMat.setTRS(pos, rot, Vec3.ONE);
523-
this.viewInvId.setValue(viewInvMat.data);
524-
}
525-
viewMat.copy(viewInvMat).invert();
526-
527-
viewProjMat.mul2(projMat, viewMat);
528-
camera.frustum.setFromMat4(viewProjMat);
529-
}
530-
531505
setBaseConstants(device, material) {
532506

533507
// Cull mode

src/scene/renderer/shadow-renderer-directional.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class ShadowRendererDirectional {
181181
shadowCam.orthoHeight = radius;
182182

183183
// cull shadow casters
184-
this.renderer.updateCameraFrustum(shadowCam);
184+
shadowCam.updateFrustum();
185185
this.shadowRenderer.cullShadowCasters(comp, light, lightRenderData.visibleCasters, shadowCam, casters);
186186

187187
const cascadeFlag = 1 << cascade;

src/scene/renderer/shadow-renderer-local.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class ShadowRendererLocal {
9191
}
9292

9393
// cull shadow casters
94-
this.renderer.updateCameraFrustum(shadowCam);
94+
shadowCam.updateFrustum();
9595
this.shadowRenderer.cullShadowCasters(comp, light, lightRenderData.visibleCasters, shadowCam, casters);
9696
}
9797
}

0 commit comments

Comments
 (0)