Skip to content

Gaussian splat spherical harmonics support #12790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

keyboardspecialist
Copy link
Contributor

Description

Adds spherical harmonic support for Gaussian splats in the SPZ format. Supports degrees 1-3 and auto detects what is available. This brings full SPZ support to CesiumJS.

Issue number and link

Testing plan

View the same asset tiled with 3 degrees of SH in the current main build versus the CI brand build:

No SH Support
SH Support

Author checklist

  • I have submitted a Contributor License Agreement
  • I have added my name to CONTRIBUTORS.md
  • I have updated CHANGES.md with a short summary of my change
  • I have added or updated unit tests to ensure consistent code coverage
  • I have updated the inline documentation, and included code examples where relevant
  • I have performed a self-review of my code

Copy link

github-actions bot commented Aug 1, 2025

Thank you for the pull request, @keyboardspecialist!

✅ We can confirm we have a CLA on file for you.

@javagl
Copy link
Contributor

javagl commented Aug 1, 2025

What is the SH degree of that example asset?

In any case, I think that proper SH support can hardly be implemented (and certainly not tested properly) based on the current SPZ loader, because it does not properly decode the SH data - unless the CesiumJS code is anticipating that wrong encoding, and compensating manually, in which case we just have to hope that it will never be fixed in the SPZ loader.

@keyboardspecialist
Copy link
Contributor Author

The underlying SPZ wasm module decodes it. The raw decoded SH buffer is all we need.

Example is degree 3

@javagl
Copy link
Contributor

javagl commented Aug 1, 2025

Asked for clarification at drumath2237/spz-loader#36 (comment)

There is an effect that is clearly visible:

Cesium Splats Cesium

Differences to BabylonJS may be due to coordinate system differences (it's difficult)

Cesium Splats Babylon

Test data:

Shs Example.zip

@keyboardspecialist
Copy link
Contributor Author

Babylon makes their lives simpler by rotating the world around the model.

The model's inverse rotation matrix is applied to the view angle which should give us the correct sampling.

One thing to keep in mind is we do apply a axis correction matrix which may contribute to what you are seeing. When I load our tiled SPZ into Babylon its sideways. That 90 degree rotation might be what you are seeing?

@javagl
Copy link
Contributor

javagl commented Aug 4, 2025

I think that the different coordinate system conventions can likely explain some differences.

Until now, I didn't dive into the maths behind the spherical harmonics. Yes, it's a bunch of coefficients that are mushed together with the view direction and eventually yield a "color". But I don't thoroughly understand the 'meaning of the values'. For example, I'd really like to create such a unit cube with spherical harmonics that looks

  1. red from the right, and cyan from the left
  2. green from the top and magenta from the bottom
  3. blue from the front and orange from the back

This could help to see whether the SHs are taken into account correctly, and whether there are any orientation issues. (If someone knows, from the tip of the head, what the SH values would have to be for that, I'd create an example - until then, this is scheduled in the 'When I have way too much time on my hands'-section of my TODO list).

@jjhembd
Copy link
Contributor

jjhembd commented Aug 4, 2025

I'll put in a +1 for the "unit cube" test data. Without that, we don't have any way of knowing if the SH are rendered correctly, or wrongly but canceled by an opposite error in the data generation, or just wrong.

Here's what I see for the 2 Sandcastles.
No SH (before the PR):
image

With SH (after this PR):
image

At first glance it looks like a change in the scene lighting direction/intensity.

@javagl
Copy link
Contributor

javagl commented Aug 4, 2025

Creating predefined test data for this is a bit tricky, though. Deriving the right values from the shader code is close to impossible - there's a reason why there is some magic "iterative learner" blackbox generating these coefficients in "real" applications. I considered a lazy/sneaky approach, and just created a GLB of such a (solid) cube, recorded a video of that spinning it in all directions, and uploaded that to poly.cam, but ... that simply refuses to generate splats from that. (Even if it worked, it might not even possible to derive the right values from that).

A bit of trial and error gave the "red-cyan" effect, but only that, and only along the diagonal...

Splats Cube

... so I think that it might be possible, and maybe even "easy" for someone who knows what he's doing. Not me, at least, not at the moment.

@ggetz
Copy link
Contributor

ggetz commented Aug 8, 2025

Hi @keyboardspecialist, what's the status here? Any input on the testing data discussion above?

@@ -23,6 +23,7 @@
- Expand the CustomShader Sample to support real-time modification of CustomShader. [#12702](https://github.com/CesiumGS/cesium/pull/12702)
- Add wrapR property to Sampler and Texture3D, to support the newly added third dimension wrap.[#12701](https://github.com/CesiumGS/cesium/pull/12701)
- Added the ability to load a specific changeset for iTwin Mesh Exports using `ITwinData.createTilesetFromIModelId` [#12778](https://github.com/CesiumGS/cesium/issues/12778)
- Added spherical harmonics support for Gaussian Splats. Supports degrees 1, 2, and 3 in the SPZ format.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can we find a spec or description of the SPZ format? Is the format stable?
A link to the spec, perhaps in GltfVertexBufferLoader, could go a long way to shortening the spin-up time for future maintainers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing that is closest to a spec is the README of https://github.com/nianticlabs/spz . The actual truth is in the code. Request for clarification are in nianticlabs/spz#42 . This does not yet cover the details of SHs and their layout, but maybe what's in the README is enough... (?)

@wongshek
Copy link

wongshek commented Aug 13, 2025

@jjhembd Here is a small ply data where the correctness of higher-order SH rendering can be clearly perceived. It would be great if it could help.

simple_board.ply.zip

Below is a comparison screenshot from Supersplat.

image image

@javagl
Copy link
Contributor

javagl commented Aug 13, 2025

For example, I'd really like to create such a unit cube with spherical harmonics that looks

red from the right, and cyan from the left
green from the top and magenta from the bottom
blue from the front and orange from the back

This could help to see whether the SHs are taken into account correctly, and whether there are any orientation issues. (If someone knows, from the tip of the head, what the SH values would have to be for that, I'd create an example - until then, this is scheduled in the 'When I have way too much time on my hands'-section of my TODO list).

I guess that it's hard to just "come up" with the proper SH coefficients for that.

So I ported the GLSL code from the GaussianSplattingViewer to Java, took the view- and projection matrix and the camera positions for the top/bottom/left/right/front/back view configuration as the inputs, the desired color as the outputs, and interpreted the whole thing as an optimization problem that I fed into the Apache BOBYQAOptimizer.
Exactly what any sane person would do 🤡

The result as ASCII PLY, SPZ, GLB, and a matching tileset JSON and Sandcastle:

Splat SH orientation experiment 2025-08-13.zip

This is what it looks like:

SplatShTest

  • blue from the front, yellow from the back
  • green from the top, magenta from the bottom
  • red from the right, cyan from the left

(This is rendered with the viewer from JSplat. BabylonJS shows the same for SPZ. For PLY, there seems to be an orientation issue, or I'm writing the coefficients in the wrong order... who knows what's "right" or "wrong" here...)

I tried to check the different views in CesiumJS:

Cesium Splats SH Test

Looks about right. The bottom is missing - let's just assume it's magenta.

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

Successfully merging this pull request may close these issues.

5 participants