Skip to content

fix(GSplat): Multiple Critical Voxelization and Compound Bugs#18496

Open
raymondyfei wants to merge 16 commits into
BabylonJS:masterfrom
raymondyfei:yfei/fix_gsplat_voxel_bug
Open

fix(GSplat): Multiple Critical Voxelization and Compound Bugs#18496
raymondyfei wants to merge 16 commits into
BabylonJS:masterfrom
raymondyfei:yfei/fix_gsplat_voxel_bug

Conversation

@raymondyfei
Copy link
Copy Markdown
Contributor

This PR has fixed a series of critical bugs about Gaussian splat rendering, in particular when voxelization and compound splats are both involved.

1. Compound mesh bounding box was wrong for voxelization

The base _updateBoundingInfo() computed bounds as local AABB × world matrix, which is
incorrect for compound meshes where each part has an independent proxy node with its own
transform. getHierarchyBoundingVectors() also never reached proxy nodes since they aren't
parented to the compound. Fixed by overriding both methods to transform all 8 corners of each
proxy's world-space AABB through the inverse compound world matrix.

2. Invisible parts inflated the voxel grid

Parts with partVisibility = 0 still contributed to scene bounds used to size the IBL shadow
voxel grid, even if they are invisible and should not be rendered. Fixed in two places: getHierarchyBoundingVectors() now skips invisible parts,
and the voxel vertex shader early-exits by pushing invisible splats outside the NDC cube
(gl_Position = vec4(2,2,2,1)).

3. SH buffers initialized to zero instead of neutral

new Uint8Array() zero-fills, but the shader's decompose() encodes 0.0 as byte 128 — so
zero decodes to -1.0. When merging a low-SH-degree mesh into a higher-SH-degree compound, padding
bytes for missing bands produced wrong colors. Extracted a shared AllocateShBuffers() helper
that pre-fills with 128, used consistently across all SH allocation sites. In this way, we can correctly merge splats with different SH degrees into a compound.

4. Wrong SH texture count in compound merge

The merge path allocated shDegree textures (one per band), but the correct count is
ceil(((degree+1)² − 1) × 3 / texelCapacity). Fixed in both the incremental and full-rebuild
merge paths.

5. G-buffer renderer misread the splat vertex data

GeometryBufferRenderer passed GSplat sub-meshes to the generic geometry shader, which
interpreted the splat index stored in position.z as a world-space Z coordinate, producing
garbage positions and normals, causing the IBL shadows to flicker randomly during rendering. Fixed with an early-exit in shouldRender() for
GaussianSplattingMesh (which GaussianSplattingCompoundMesh returns by design, covering both) since it does NOT support G-buffer renderer yet.

6. needsRotationScaleTextures not forwarded; missing _shDegree

GaussianSplattingCompoundMesh's constructor didn't expose needsRotationScaleTextures, and
enabling it post-load called updateData() without this._shDegree, causing the compound mesh to ignore any spherical harmonics when later attached as caster to an IBL shadow (which triggers the updateData()). Both fixed.

7. Proxy view used texture count instead of SH degree

_makeViewForProxy set _shDegree to this._shData?.length (number of textures) instead of
this._shDegree (actual degree). These differ because multiple bands share a texture. Fixed.

8. IBL shadows updateSceneBounds guard ordering

The empty-mesh guard ran after voxelGridSize was computed from sentinel values. When all
parts were hidden, the bounds stayed at the sentinels and triggered a spurious warning. Fixed:
early-return before touching voxelGridSize when no meshes; silent return preserving the last
valid scale when bounds are sentinels.

Tests

Three new screenshot tests covering the edge cases that previously failed:

  • rendering single-part GSplat but in a compound, with IBL shadow;
  • multi-part compound GSplat with IBL shadow
  • multi-part compound GSplat, but with only one GSplat visible, with IBL shadow

@raymondyfei raymondyfei changed the title Yfei/fix gsplat voxel bug fix(GSplat): Multiple Critical Voxelization and Compound Bugs May 22, 2026
@Popov72
Copy link
Copy Markdown
Contributor

Popov72 commented May 22, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

Snapshot stored with reference name:
refs/pull/18496/merge

Test environment:
https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/18496/merge/index.html

To test a playground add it to the URL, for example:

https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/18496/merge/index.html#WGZLGJ#4600

Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves):

https://playground.babylonjs.com/?snapshot=refs/pull/18496/merge
https://sandbox.babylonjs.com/?snapshot=refs/pull/18496/merge
https://gui.babylonjs.com/?snapshot=refs/pull/18496/merge
https://nme.babylonjs.com/?snapshot=refs/pull/18496/merge

To test the snapshot in the playground with a playground ID add it after the snapshot query string:

https://playground.babylonjs.com/?snapshot=refs/pull/18496/merge#BCU1XR#0

If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools.

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

🟢 Memory Leak Test Results

13 passed, 0 leaked out of 13 scenarios

🟢 All memory leak tests passed — no leaks detected.

Passed Scenarios (13)
Scenario Package
Core Feature Stack @babylonjs/core
Core Rendering Materials Shadows Stack @babylonjs/core
Core Textures Render Targets PostProcess Stack @babylonjs/core
GUI Fullscreen UI Controls @babylonjs/gui
GUI Mesh ADT Controls @babylonjs/gui
Loaders Boombox Import @babylonjs/loaders
Loaders OBJ Direct Load @babylonjs/loaders
Loaders STL Direct Load @babylonjs/loaders
Materials Library Stack @babylonjs/materials
Serializers glTF Export @babylonjs/serializers
Serializers GLB Export @babylonjs/serializers
PostProcesses Digital Rain Stack @babylonjs/post-processes
Procedural Textures Stack @babylonjs/procedural-textures

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

⚡ Performance Test Results

🟢 All performance tests passed — no regressions detected.

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 22, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants