Skip to content
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

Add 3D gaussian splats support #5658

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

Conversation

dmarcos
Copy link
Member

@dmarcos dmarcos commented Feb 9, 2025

Last big item for 1.7.0 release. Still work in progress. Based on https://github.com/quadjr/aframe-gaussian-splatting implementation

I adapted style, made a few minor tweaks and commented thoroughly. I still don't fully understand some of the details. Good opportunity to deepen understanding of 3D gaussian splats for those interested. Before merge we should thoroughly understand the code and optimize.

I recommend getting acquainted with the original paper: https://arxiv.org/pdf/2308.04079

In the code comments you'll see references to other relevant literature.

In VR on a Quest 3 when the sample splat is fully loaded (~1.1M splats rendered) I get ~14fps.

Quick overview:

  1. As output of the training process splats are represented as ellipsoids with a position, scale, color and shape that are later projected to 2D ellipses to be renderer on screen
  2. Each splat is rendered as a quad with a color that gets positioned, scaled and shaped following each ellipse characteristics.
  3. Splats are sorted in a worker. Splats are instances and we have to sort them out to render them in the correct order (back to front)
  4. Current implementation doesn't support spherical harmonics. Color of splats varies depending of point of view vs a fixed color.

Comment on lines +9 to +10
pixelRatio: {type: 'number', default: 1},
xrPixelRatio: {type: 'number', default: 0.5}
Copy link
Contributor

Choose a reason for hiding this comment

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

These two properties don't belong on the component. They should probably be made part of the renderer system. Though the pixelRatio is a bit tricky given that we default to window.devicePixelRatio, so maybe express it as a factor on top of the devicePixelRatio?

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah. don't love the API yet either

if (data.xrPixelRatio > 0) {
sceneEl.renderer.xr.setFramebufferScaleFactor(this.data.xrPixelRatio);
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Update doesn't handle changes to cutoutEntity.

bytesDownloaded += newChunk.length;
chunks.push(newChunk);

// Downloar progress stats.
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo

Suggested change
// Downloar progress stats.
// Download progress stats.

try {
var dataReceived = await reader.read();
if (dataReceived.done) {
console.log('Completed download.');
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer no logging from components, but if we do it should be using debug.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah. I'll remove. still WIP.


initGL: async function initGL (numVertices) {
console.log('initGL', numVertices);
this.el.object3D.frustumCulled = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem necessary. The actual mesh has frustum culling disabled as well further down in this method.

Comment on lines 273 to 282
// Wait until texture is ready
while (true) {
var centerAndScaleTextureProperties = renderer.properties.get(this.centerAndScaleTexture);
var covAndColorTextureProperties = renderer.properties.get(this.covAndColorTexture);
if (centerAndScaleTextureProperties && centerAndScaleTextureProperties.__webglTexture &&
covAndColorTextureProperties && centerAndScaleTextureProperties.__webglTexture) {
break;
}
await new Promise(resolve => setTimeout(resolve, 10));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of this polling it should just tell Three.js to initialize the textures: https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderer.initTexture

.eslintrc.json Outdated
@@ -83,6 +83,13 @@
"ecmaVersion": 6
}
},
{
/* This module uses ES8 async / await due to WebXR Anchor Module integration */
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment incorrectly refers to WebXR Anchor Module. That being said, I don't believe async/await is a hard requirement for splat.js and it can be rewritten in the A-Frame code style for consistency, avoiding this exception altogether.

@mrxz
Copy link
Contributor

mrxz commented Feb 9, 2025

My main concern with a splat component in the core is that things evolve quickly, so we'd might end up with a component that is quickly outdated. At the very least let's explicitly name it something like 3d-gaussian-splat, to not confuse it with 2DGS or other splatting techniques.

I haven't really been following Gaussian splatting developments all that closely, but quadjr's aframe component is pretty old. How does it compare to implementations in PlayCanvas, 8thwall/Scaniverse or gsplat.js? It seems that most have switched to compressed and quantized formats, although it seems no clear standard file format has emerged.

Based on https://github.com/quadjr/aframe-gaussian-splatting implementation

Please include the original license at the start of this file.

In VR on a Quest 3 when the sample splat is fully loaded (~1.1M splats rendered) I get ~14fps.

Into the Scaniverse (https://www.intothescaniverse.com/) mostly runs at 72fps on a Quest 3, so that's probably something we should aim for as well. Obviously depends on the amount of splats amongst other factors, but a small object-centered example would probably work better. Could even make it grabbable.

@arpu
Copy link
Contributor

arpu commented Feb 9, 2025

from my perspective its to early to add this in the main branch, better to external git, ther eis a lot of discussion going on to bring splats to gltf/glb ( with mesh opt compression)

KhronosGroup/glTF#2454

https://github.com/CesiumGS/glTF/tree/proposal-KHR_gaussian_splatting/extensions/2.0/Khronos/KHR_gaussian_splatting

@dmarcos
Copy link
Member Author

dmarcos commented Feb 9, 2025

from my perspective its to early to add this in the main branch, better to external git, ther eis a lot of discussion going on to bring splats to gltf/glb ( with mesh opt compression)

KhronosGroup/glTF#2454

https://github.com/CesiumGS/glTF/tree/proposal-KHR_gaussian_splatting/extensions/2.0/Khronos/KHR_gaussian_splatting

A-Frame role has always been to be at the forefront: We built on custom elements v1 before being standard. We also shipped WebVR, WebXR + extensions (hand tracking, layers, immersive navigation…) and gltf before becoming standard.

Standards are better designed when based on real world usage and feedback. Should be practice consolidation not prescription. Standard for gs3d probably premature.

A-Frame mission is to make 3D content creation accesible to anyone. I see exciting opportunities at the intersection with AI. I would love for A-Frame to participate and be top of mind of researchers and early adopters.

@koktavy
Copy link

koktavy commented Feb 10, 2025

It would be very exciting to see A-Frame adopt 3DGS, even as part of an extras/experimental package. And A-Frame implementation of @mkkellogg's Three.js 3DGS viewer might be a better path to keep up with active development efforts.

https://github.com/mkkellogg/GaussianSplats3D/tree/main

@dmarcos
Copy link
Member Author

dmarcos commented Feb 10, 2025

It would be very exciting to see A-Frame adopt 3DGS, even as part of an extras/experimental package. And A-Frame implementation of @mkkellogg's Three.js 3DGS viewer might be a better path to keep up with active development efforts.

https://github.com/mkkellogg/GaussianSplats3D/tree/main

Thanks! I'm excited too.

Looked at mkkellog implementation but it's pretty complicated / advanced already. I went with https://github.com/quadjr/aframe-gaussian-splatting that is a very minimal port of the original (first?) WebGL implementation https://github.com/antimatter15/splat

My goal is to first have a minimal and performant implementation that we can later use as a base for more complex stuff. I would love to later have an example of a nice and simple viewer with great controls on every platform.

@mrxz
Copy link
Contributor

mrxz commented Feb 10, 2025

My goal is to first have a minimal and performant implementation that we can later use as a base for more complex stuff. I would love to later have an example of a nice and simple viewer with great controls on every platform.

A good chunk of the complexity of mkkellogg/GaussianSplats3D seems to be to make it performant and to support a wide array of formats/splat types. Both things that I'd argue are essential for a splat component in A-Frame to be useful.

I don't really see the benefit in re-inventing the wheel in this case. Rendering of Gaussian splats is very expensive, so unless/until performance is on-par, wouldn't user just create their own components or use a community made one that does wrap any of the more mature implementations out there?

@dmarcos
Copy link
Member Author

dmarcos commented Feb 10, 2025

My goal is to first have a minimal and performant implementation that we can later use as a base for more complex stuff. I would love to later have an example of a nice and simple viewer with great controls on every platform.

A good chunk of the complexity of mkkellogg/GaussianSplats3D seems to be to make it performant and to support a wide array of formats/splat types. Both things that I'd argue are essential for a splat component in A-Frame to be useful.

I don't really see the benefit in re-inventing the wheel in this case. Rendering of Gaussian splats is very expensive, so unless/until performance is on-par, wouldn't user just create their own components or use a community made one that does wrap any of the more mature implementations out there?

Wheel far from invented. Stuff evolving quick. mkkellogg performance is ~24fps on Quest 3 on the simple demo scene. Playcanvas ~17fps (similar to ours) on the same truck splat on this PR's example. Scenes from Into the Scaniverse are the only ones I've seen decently performing on Quest 3 (~72fps). Presumably cherry picked.

I want to develop understanding of the problem space before taking on any dependency. A simple single file implementation like this PR is a good starting point before incorporating anything with a lot of decisions made for us that we don't fully understand.

@dmarcos
Copy link
Member Author

dmarcos commented Feb 11, 2025

I limited the number of splats rendered at any given time by adding the follow statement in line 532

 if (validCount > 150000) { break; }

We can render at 90fps on Quest3 up to ~150k splats.

@mrxz
Copy link
Contributor

mrxz commented Feb 11, 2025

Wheel far from invented. Stuff evolving quick.

On the training, compression, rendering and sorting side there's plenty to experiment with. But this is about making it a core component of A-Frame. From the perspective of the user the relevance is being able to add "splats" to their scene. People will expect to be able to use the output of tools like SuperSplat and Scaniverse and have it perform comparable as well.

In other words, we'd end up implementing loading of compressed .PLY files, .SPZ files, supporting the spherical harmonics for view dependent lighting and implementing the sorting in WASM using SharedArrayBuffer, progressive loading, perhaps even 2DGS support. All things that have been done numerous times.

For experimenting, the core isn't the right place IMO. And for providing a component for users, we'd end up implementing (and maintaining!) a lot of features. I agree with @koktavy that wrapping mkkellogg/GaussianSplats3D is a better path to stay up to date. We can always contribute upstream if we do find improvements.

mkkellogg performance is ~24fps on Quest 3 on the simple demo scene. Playcanvas ~17fps (similar to ours) on the same truck splat on this PR's example.

Both of those render at full resolution. I'm not able to reproduce ~17fps using the component in this PR when setting the xrPixelRatio to 1.0, so there's quite a bit of catching-up to do. Ideally there'd be an apples-to-apples comparison with various implementations all rendering the same scene under the same conditions.

Scenes from Into the Scaniverse are the only ones I've seen decently performing on Quest 3 (~72fps). Presumably cherry picked.

Not sure if they really cherry pick them, as they do allow users to create their own and post them on the map. From what I gather they do a lot of pre-processing. One thing I have noticed is that the background is actually static despite the Gaussian Splat appearance. This presumably helps quite a bit as it saves on the number of splats as well as reduces overdraw caused by large splats in the background.

We can render at 90fps on Quest3 up to ~150k splats.

Splats don't have a fixed cost associated with them. They vary in size and spatial distribution. Overdraw really kills performance AFAICT, so ~150k splats representing a "full scene" or ~150k focussed on a single object will likely perform wildly different.

@dmarcos
Copy link
Member Author

dmarcos commented Feb 12, 2025

Any example of mkkellog working at least 72fps on Quest? At this point broad support of formats, or spherical harmonics are not top priority. Just interested on compelling examples that perform well and are interesting to look at.

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.

4 participants