What happened?
For a node with EXT_mesh_gpu_instancing, the bounding sphere of each primitive's draw command is computed from the canonical mesh's POSITION min/max plus the instance TRANSLATION min/max only (ModelUtility.getPositionMinMax, called from PrimitiveRenderResources). Instance ROTATION and SCALE are ignored, and ModelSceneGraph.computeBoundingVolumes builds the model bounding sphere from the same min/max.
When the canonical mesh is not centred on the node origin and the instances are rotated, the rendered geometry is at R * p + t while the sphere assumes p + t, so the sphere can be hundreds of metres from the geometry. With the default cull: true the model is frustum culled whenever that phantom sphere leaves the view, even though the instances are in the middle of the screen. For 3D Tiles content (cull: false) the sphere still feeds the near/far binning in View.createPotentiallyVisibleSet, and insertIntoBin drops the command whenever the sphere lies entirely behind the camera, so whole instance groups disappear when zooming in from one side and reappear from the other side, from below, or when zooming out. zoomTo and debugShowBoundingVolume place the sphere in the wrong position.
This layout is common in tilers that instance repeated meshes: the canonical copy keeps its source-local position (for example the first occurrence's world position) and the other instances are rotated onto it. Instances with SCALE but no ROTATION are affected too, because the scale attribute is uploaded as a GPU buffer only and never read on the CPU: a box spanning [-1, 1] on each axis scaled by 10 keeps a bounding sphere radius of 1.73 instead of 17.32.
Expected: the bounding sphere contains every instance after applying its full transform, so a draw command is never culled or dropped while its instances are visible.
This is the instancing case raised in #12108 (#12108 (comment)). Legacy i3dm world-space instancing (#11176, #12105) is a separate problem and is not covered here. A fix is ready and will follow as a pull request.
Reproduction steps
- Run the Sandcastle below. It builds an in-memory glTF: one unit cube whose vertices are centred at (200, 0, 0) in glTF space, instanced three times with a 180 degree rotation about the glTF Y axis and translations (200, 0, z), so the three cubes render next to the node origin (about 3 m east of it in the local east/north/up frame after Cesium's axis correction). A yellow marker and label show where the cubes are.
- The initial view ("1. Phantom sphere in view") looks at the cubes from 25 m: the three cubes render, and
debugShowBoundingVolume draws the model's bounding sphere as a red dot near the horizon, 400 m north of them. The console line reports the sphere centre 400 m from the rendered cubes and 0 m from the unrotated position.
- Press "2. Phantom sphere behind camera": the camera looks at the cubes from the other side. The sphere is now behind the camera, the model is frustum culled and only the yellow marker remains, although the cubes are in the middle of the view.
- Rotate the view by hand: the cubes appear and disappear as the phantom sphere enters and leaves the frustum.
The same happens with EXT_mesh_gpu_instancing content in a 3D Tiles tileset: the tile is selected and loaded, but the instanced draw command is dropped by the near/far binning whenever the camera moves past the phantom sphere.
Sandcastle example
https://sandcastle.cesium.com/#c=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
Environment
Browser: Microsoft Edge 153.0.4234.48 (Official build) (64-bit); also reproduced in headless Chromium 151 on Linux
OS: Windows 11
CesiumJS version: 1.145.0; the same code is present in 1.139.1 and in main
AI acknowledgment
What happened?
For a node with
EXT_mesh_gpu_instancing, the bounding sphere of each primitive's draw command is computed from the canonical mesh'sPOSITIONmin/max plus the instanceTRANSLATIONmin/max only (ModelUtility.getPositionMinMax, called fromPrimitiveRenderResources). InstanceROTATIONandSCALEare ignored, andModelSceneGraph.computeBoundingVolumesbuilds the model bounding sphere from the same min/max.When the canonical mesh is not centred on the node origin and the instances are rotated, the rendered geometry is at
R * p + twhile the sphere assumesp + t, so the sphere can be hundreds of metres from the geometry. With the defaultcull: truethe model is frustum culled whenever that phantom sphere leaves the view, even though the instances are in the middle of the screen. For 3D Tiles content (cull: false) the sphere still feeds the near/far binning inView.createPotentiallyVisibleSet, andinsertIntoBindrops the command whenever the sphere lies entirely behind the camera, so whole instance groups disappear when zooming in from one side and reappear from the other side, from below, or when zooming out.zoomToanddebugShowBoundingVolumeplace the sphere in the wrong position.This layout is common in tilers that instance repeated meshes: the canonical copy keeps its source-local position (for example the first occurrence's world position) and the other instances are rotated onto it. Instances with
SCALEbut noROTATIONare affected too, because the scale attribute is uploaded as a GPU buffer only and never read on the CPU: a box spanning [-1, 1] on each axis scaled by 10 keeps a bounding sphere radius of 1.73 instead of 17.32.Expected: the bounding sphere contains every instance after applying its full transform, so a draw command is never culled or dropped while its instances are visible.
This is the instancing case raised in #12108 (#12108 (comment)). Legacy i3dm world-space instancing (#11176, #12105) is a separate problem and is not covered here. A fix is ready and will follow as a pull request.
Reproduction steps
debugShowBoundingVolumedraws the model's bounding sphere as a red dot near the horizon, 400 m north of them. The console line reports the sphere centre 400 m from the rendered cubes and 0 m from the unrotated position.The same happens with
EXT_mesh_gpu_instancingcontent in a 3D Tiles tileset: the tile is selected and loaded, but the instanced draw command is dropped by the near/far binning whenever the camera moves past the phantom sphere.Sandcastle example
https://sandcastle.cesium.com/#c=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
Environment
Browser: Microsoft Edge 153.0.4234.48 (Official build) (64-bit); also reproduced in headless Chromium 151 on Linux
OS: Windows 11
CesiumJS version: 1.145.0; the same code is present in 1.139.1 and in
mainAI acknowledgment