{{ message }}
Repository navigation
Conversation
2500 3D model buildings on a grid, 40 moving cars, a light casting shadows and a camera driving through the city: like a driving game, most of the city can't be seen at any time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FDUqntVYfmRadSTSqSDDPE
Three.js goes through all the meshes of a scene at each render: to update their matrices, and to find the ones to render for the camera and for each shadow map. With a large city, most of the frame was spent on objects that can't be seen. Before rendering a 3D layer, the 3D models and cubes whose box is out of the camera frustum and of the shadow cameras are now hidden, and their matrices are only updated when they move (for raycasts). Objects that can render outside of their box (animated models, objects with children added by extensions, custom objects...) are never hidden, and culling is disabled when lights casting shadows can't be checked (point lights, lights in objects), in the in-game editor and in VR. The 3d-car-coin-hunt example with a city 10 times larger (2800 objects): 42ms -> 23ms per frame, with identical renders and state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FDUqntVYfmRadSTSqSDDPE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FDUqntVYfmRadSTSqSDDPE
Contributor
⏱️ Game engine benchmarksComparing
Times are the median duration of a batch of iterations (micro benchmarks) or of a frame (scene benchmarks). A change is reported only when larger than 5% and measured by every pair of rounds. |
An object culled before it ever moved kept the matrices of its last render (or none), so raycasts missed it. Extensions calling `updateMatrixWorld` on a culled object that moved also read stale matrices. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FDUqntVYfmRadSTSqSDDPE
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Work in progress. This makes large 3D scenes scale with what the camera sees, rather than with the number of objects. It's measured on the 3d-car-coin-hunt example with a city 10 times larger, and with a new CI benchmark.
Measurements: 3d-car-coin-hunt with a 10x larger city
The city of the example (buildings, roads, trees, parked cars, coins) is repeated in 5x2 tiles, giving 2800 instances. The game is exported with the CLI of the portable build and played headlessly with scripted inputs (driving around) through the gameplay test harness, rendering without a GPU.
Why it was slow: draw calls barely grow with the city, because Three.js already frustum-culls each mesh. But at each render, Three.js goes through every mesh of the scene (about 20,000 nodes at x10) to:
Changes
layer-pixi-renderer.ts): before rendering a 3D layer, 3D models and cubes whose box is outside the camera frustum, and outside the frustums of the shadow cameras (directional and spot lights), are hidden (visible = false). Three.js then skips their meshes.updateMatrixWorldon them, so raycasts (like the Raycaster3D extension) and extensions reading positions stay correct.isRenderedInsideItsBox(). That covers models with an animation, objects whose root has children added by extensions (like lights), custom objects and extension objects.Checks
tests/Extensions/3d-culling.js):Known limits
Next steps measured, not in this PR
InstancedMeshper mesh of each model, filled every frame with the visible instances only, and hide the original meshes (kept for raycasts).IsActivecall after the step and the polling of 9 getters before it; behaviors without steps could be skipped by the scene loop.🤖 Generated with Claude Code
https://claude.ai/code/session_01FDUqntVYfmRadSTSqSDDPE