Giter Club home page Giter Club logo

gaussiansplats3d's Introduction

3D Gaussian splatting for Three.js

Three.js-based implemetation of a renderer for 3D Gaussian Splatting for Real-Time Radiance Field Rendering, a technique for generating 3D scenes from 2D images. Their project is CUDA-based and needs to run natively on your machine, but I wanted to build a viewer that was accessible via the web.

The 3D scenes are stored in a format similar to point clouds and can be viewed, navigated, and interacted with in real-time. This renderer will work with the .ply files generated by the INRIA project, standard .splat files, or my own custom .ksplat files, which are a trimmed-down and compressed version of the original .ply files.

When I started, web-based viewers were already available -- A WebGL-based viewer from antimatter15 and a WebGPU viewer from cvlab-epfl -- However no Three.js version existed. I used those versions as a starting point for my initial implementation, but as of now this project contains all my own code.

Highlights

  • Rendering is done entirely through Three.js
  • Code is organized into modern ES modules
  • Built-in viewer is self-contained so very little code is necessary to load and view a scene
  • Viewer can import .ply files, .splat files, or my custom compressed .ksplat files
  • Users can convert .ply or .splat files to the .ksplat file format
  • Allows a Three.js scene or object group to be rendered along with the splats
  • Built-in WebXR support
  • Supports 1st and 2nd degree spherical harmonics for view-dependent effects
  • Focus on optimization:
    • Splats culled prior to sorting & rendering using a custom octree
    • WASM splat sort: Implemented in C++ using WASM SIMD instructions
    • Partially GPU accelerated splat sort: Uses transform feedback to pre-calculate splat distances

Tips

  • Progressively loaded .ply and .splat files will not have certain optimizations such as cache-optimized splat ordering applied to them. For optimial performance, convert these file types to .ksplat or load them non-progressively.
  • Converting your scenes to .ksplat will result in the fastest loading times since its format matches the internal format for splat data.
  • Scenes with large dimensions or high splat density will cause issues with the default settings. For those scenes, you can try a couple of things:
    • Set the viewer parameter integerBasedSort to false to force a slower, floating-point based splat sort.
    • Experiment with a larger value for viewer parameter splatSortDistanceMapPrecision, to adjust the precision for the distance map in the splat sort. Larger precision values will result in reduced performance, but often can alleviate visual artifacts that arise when the precision is too low.

Known issues

  • Splat sort runs on the CPU – would be great to figure out a GPU-based approach
  • Artifacts are visible when you move or rotate too fast (due to CPU-based splat sort)
  • Sub-optimal performance on mobile devices
  • Custom .ksplat file format still needs work, especially around compression
  • Scenes with very large dimensions will probably crash (often with an Index out of bounds error from the splat sort). Changing splatSortDistanceMapPrecision or integerBasedSort will probably not help in those cases.

Limitations

Currently there are limits on the number of splats that can be rendered, and those limits depend mainly on the degree of spherical harmonics desired. Those limits are:

Spherical harmonics degree Max splat count
0 ~ 16,000,000
1 ~ 11,000,000
2 ~ 8,000,000

Future work will include optimizing how splat data is packed into data textures, which will help increase these limits.

Future work

This is still very much a work in progress! There are several things that still need to be done:

  • Improve the way splat data is packed into data textures
  • Continue optimizing CPU-based splat sort - maybe try an incremental sort of some kind?
  • Support very large scenes (streaming sections & LOD)

Online demo

https://projects.markkellogg.org/threejs/demo_gaussian_splats_3d.php

Controls

Mouse

  • Left click to set the focal point
  • Left click and drag to orbit around the focal point
  • Right click and drag to pan the camera and focal point

Keyboard

  • C Toggles the mesh cursor, showing the intersection point of a mouse-projected ray and the splat mesh

  • I Toggles an info panel that displays debugging info:

    • Camera position
    • Camera focal point/look-at point
    • Camera up vector
    • Mesh cursor position
    • Current FPS
    • Renderer window size
    • Ratio of rendered splats to total splats
    • Last splat sort duration
  • U Toggles a debug object that shows the orientation of the camera controls. It includes a green arrow representing the camera's orbital axis and a white square representing the plane at which the camera's elevation angle is 0.

  • Left arrow Rotate the camera's up vector counter-clockwise

  • Right arrow Rotate the camera's up vector clockwise

  • P Toggle point-cloud mode, where each splat is rendered as a filled circle

  • = Increase splat scale

  • - Decrease splat scale

  • O Toggle orthographic mode


Building from source and running locally

Navigate to the code directory and run

npm install

Next run the build. For Linux & Mac OS systems run:

npm run build

For Windows I have added a Windows-compatible version of the build command:

npm run build-windows

To view the demo scenes locally run

npm run demo

The demo will be accessible locally at http://127.0.0.1:8080/index.html. You will need to download the data for the demo scenes and extract them into

<code directory>/build/demo/assets/data

The demo scene data is available here: https://projects.markkellogg.org/downloads/gaussian_splat_data.zip

Installing as an NPM package

If you don't want to build the library from source, it is also available as an NPM package. The NPM package does not come with the source code or demos that are available in the source repository. To install, run the following command:

npm install @mkkellogg/gaussian-splats-3d

Basic Usage

To run the built-in viewer:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';

const viewer = new GaussianSplats3D.Viewer({
    'cameraUp': [0, -1, -0.6],
    'initialCameraPosition': [-1, -4, 6],
    'initialCameraLookAt': [0, 4, 0]
});
viewer.addSplatScene('<path to .ply, .ksplat, or .splat file>', {
    'splatAlphaRemovalThreshold': 5,
    'showLoadingUI': true,
    'position': [0, 1, 0],
    'rotation': [0, 0, 0, 1],
    'scale': [1.5, 1.5, 1.5]
})
.then(() => {
    viewer.start();
});

Viewer parameters

Parameter Purpose
cameraUp The natural 'up' vector for viewing the scene (only has an effect when used with orbit controls and when the viewer uses its own camera). Serves as the axis around which the camera will orbit, and is used to determine the scene's orientation relative to the camera.
initialCameraPosition The camera's initial position (only used when the viewer uses its own camera).
initialCameraLookAt The initial focal point of the camera and center of the camera's orbit (only used when the viewer uses its own camera).

Parameters for addSplatScene()

Parameter Purpose
format Force the loader to assume the specified file format when loading a splat scene. This is useful when loading from a URL where there is no file extension. Valid values are defined in the SceneFormat enum: Ply, Splat, and KSplat.
splatAlphaRemovalThreshold Tells addSplatScene() to ignore any splats with an alpha less than the specified value (valid range: 0 - 255). Defaults to 1.
showLoadingUI Displays a loading spinner and/or loading progress bar while the scene is loading. Defaults to true.
position Position of the scene, acts as an offset from its default position. Defaults to [0, 0, 0].
rotation Rotation of the scene represented as a quaternion, defaults to [0, 0, 0, 1] (identity quaternion).
scale Scene's scale, defaults to [1, 1, 1].
progressiveLoad Progressively load the scene's splat data and allow the scene to be rendered and viewed as the splats are loaded. Option is only valid for addSplatScene(), and not for addSplatScenes().

Viewer can also load multiple scenes simultaneously with the addSplatScenes() function:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';

viewer.addSplatScenes([{
        'path': '<path to first .ply, .ksplat, or .splat file>',
        'splatAlphaRemovalThreshold': 20
    },
    {
        'path': '<path to second .ply, .ksplat, or .splat file>',
        'rotation': [-0.14724434, -0.0761755, 0.1410657, 0.976020],
        'scale': [1.5, 1.5, 1.5],
        'position': [-3, -2, -3.2]
    }
])
.then(() => {
    viewer.start();
});

The addSplatScene() and addSplatScenes() methods will accept the original .ply files, standard .splat files, and my custom .ksplat files.


Integrating THREE.js scenes

You can integrate your own Three.js scene into the viewer if you want rendering to be handled for you. Just pass a Three.js scene object as the threeScene parameter to the constructor:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
import * as THREE from 'three';

const threeScene = new THREE.Scene();
const boxColor = 0xBBBBBB;
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
const boxMesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({'color': boxColor}));
boxMesh.position.set(3, 2, 2);
threeScene.add(boxMesh);

const viewer = new GaussianSplats3D.Viewer({
    'threeScene': threeScene,
});
viewer.addSplatScene('<path to .ply, .ksplat, or .splat file>')
.then(() => {
    viewer.start();
});

Currently this will only work for objects that write to the depth buffer (e.g. standard opaque objects). Supporting transparent objects will be more challenging :)

A "drop-in" mode for the viewer is also supported. The DropInViewer class encapsulates Viewer and can be added to a Three.js scene like any other renderable:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
import * as THREE from 'three';

const threeScene = new THREE.Scene();
const viewer = new GaussianSplats3D.DropInViewer({
    'gpuAcceleratedSort': true
});
viewer.addSplatScenes([{
        'path': '<path to .ply, .ksplat, or .splat file>'
        'splatAlphaRemovalThreshold': 5
    },
    {
        'path': '<path to .ply, .ksplat, or .splat file>',
        'rotation': [0, -0.857, -0.514495, 6.123233995736766e-17],
        'scale': [1.5, 1.5, 1.5],
        'position': [0, -2, -1.2]
    }
]);
threeScene.add(viewer);

Advanced options

The viewer allows for various levels of customization via constructor parameters. You can control when its update() and render() methods are called by passing false for the selfDrivenMode parameter and then calling those methods whenever/wherever you decide is appropriate. You can also use your own camera controls, as well as an your own instance of a Three.js Renderer or Camera The sample below shows all of these options:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
import * as THREE from 'three';

const renderWidth = 800;
const renderHeight = 600;

const rootElement = document.createElement('div');
rootElement.style.width = renderWidth + 'px';
rootElement.style.height = renderHeight + 'px';
document.body.appendChild(rootElement);

const renderer = new THREE.WebGLRenderer({
    antialias: false
});
renderer.setSize(renderWidth, renderHeight);
rootElement.appendChild(renderer.domElement);

const camera = new THREE.PerspectiveCamera(65, renderWidth / renderHeight, 0.1, 500);
camera.position.copy(new THREE.Vector3().fromArray([-1, -4, 6]));
camera.up = new THREE.Vector3().fromArray([0, -1, -0.6]).normalize();
camera.lookAt(new THREE.Vector3().fromArray([0, 4, -0]));

const viewer = new GaussianSplats3D.Viewer({
    'selfDrivenMode': false,
    'renderer': renderer,
    'camera': camera,
    'useBuiltInControls': false,
    'ignoreDevicePixelRatio': false,
    'gpuAcceleratedSort': true,
    `enableSIMDInSort`: true,
    'sharedMemoryForWorkers': true,
    'integerBasedSort': true,
    'halfPrecisionCovariancesOnGPU': true,
    'dynamicScene': false,
    'webXRMode': GaussianSplats3D.WebXRMode.None,
    'renderMode': GaussianSplats3D.RenderMode.OnChange,
    'sceneRevealMode': GaussianSplats3D.SceneRevealMode.Instant,
    'antialiased': false,
    'focalAdjustment': 1.0,
    'logLevel': GaussianSplats3D.LogLevel.None,
    'sphericalHarmonicsDegree': 0,
    `enableOptionalEffects`: false,
    `inMemoryCompressionLevel`: 2
    `freeIntermediateSplatData`: false
});
viewer.addSplatScene('<path to .ply, .ksplat, or .splat file>')
.then(() => {
    requestAnimationFrame(update);
});

Since selfDrivenMode is false, it is up to the developer to call the update() and render() methods on the Viewer class:

function update() {
    requestAnimationFrame(update);
    viewer.update();
    viewer.render();
}

Advanced Viewer parameters

Parameter Purpose
selfDrivenMode If false, tells the viewer that you will manually call its update() and render() methods. Defaults to true.
renderer Pass an instance of a Three.js Renderer to the viewer, otherwise it will create its own. Defaults to undefined.
camera Pass an instance of a Three.js Camera to the viewer, otherwise it will create its own. Defaults to undefined.
useBuiltInControls Tells the viewer to use its own camera controls. Defaults to true.
ignoreDevicePixelRatio Tells the viewer to pretend the device pixel ratio is 1, which can boost performance on devices where it is larger, at a small cost to visual quality. Defaults to false.
gpuAcceleratedSort Tells the viewer to use a partially GPU-accelerated approach to sorting splats. Currently this means pre-computation of splat distances from the camera is performed on the GPU. It is recommended that this only be set to true when sharedMemoryForWorkers is also true. Defaults to false on mobile devices, true otherwise.
enableSIMDInSort Enable the usage of SIMD WebAssembly instructions for the splat sort. Default is true.
sharedMemoryForWorkers Tells the viewer to use shared memory via a SharedArrayBuffer to transfer data to and from the sorting web worker. If set to false, it is recommended that gpuAcceleratedSort be set to false as well. Defaults to true.
integerBasedSort Tells the sorting web worker to use the integer versions of relevant data to compute the distance of splats from the camera. Since integer arithmetic is faster than floating point, this reduces sort time. However it can result in integer overflows in larger scenes so it should only be used for small scenes. Defaults to true.
splatSortDistanceMapPrecision Specify the precision for the distance map used in the splat sort algorithm. Defaults to 16 (16-bit). A lower precision is faster, but may result in visual artifacts in larger or denser scenes.
halfPrecisionCovariancesOnGPU Tells the viewer to use 16-bit floating point values when storing splat covariance data in textures, instead of 32-bit. Defaults to false.
dynamicScene Tells the viewer to not make any optimizations that depend on the scene being static. Additionally all splat data retrieved from the viewer's splat mesh will not have their respective scene transform applied to them by default.
webXRMode Tells the viewer whether or not to enable built-in Web VR or Web AR. Valid values are defined in the WebXRMode enum: None, VR, and AR. Defaults to None.
webXRSessionInit Tells the viewer to build a WebXR session with some options. Defaults with {}. For more details : https://developer.mozilla.org/en-US/docs/Web/API/XRSystem/requestSession#options
renderMode Controls when the viewer renders the scene. Valid values are defined in the RenderMode enum: Always, OnChange, and Never. Defaults to Always.
sceneRevealMode Controls the fade-in effect used when the scene is loaded. Valid values are defined in the SceneRevealMode enum: Default, Gradual, and Instant. Default results in a nice, slow fade-in effect for progressively loaded scenes, and a fast fade-in for non progressively loaded scenes. Gradual will force a slow fade-in for all scenes. Instant will force all loaded scene data to be immediately visible.
antialiased When true, will perform additional steps during rendering to address artifacts caused by the rendering of gaussians at substantially different resolutions than that at which they were rendered during training. This will only work correctly for models that were trained using a process that utilizes this compensation calculation. For more details: nerfstudio-project/gsplat#117, graphdeco-inria/gaussian-splatting#294 (comment)
focalAdjustment Hacky, non-scientific parameter for tweaking focal length related calculations. For scenes with very small gaussians & small details, increasing this value can help improve visual quality. Default value is 1.0.
logLevel Verbosity of the console logging. Defaults to GaussianSplats3D.LogLevel.None.
sphericalHarmonicsDegree Degree of spherical harmonics to utilize in rendering splats (assuming the data is present in the splat scene). Valid values are 0, 1, or 2. Default value is 0.
enableOptionalEffects When true, allows for usage of extra properties and attributes during rendering for effects such as opacity adjustment. Default is false for performance reasons. These properties are separate from transform properties (scale, rotation, position) that are enabled by the dynamicScene parameter.
inMemoryCompressionLevel Level to compress .ply or .ksplat files when loading them for direct rendering (not exporting to .ksplat). Valid values are the same as .ksplat compression levels (0, 1, or 2). Default is 0.
optimizeSplatData Reorder splat data in memory after loading is complete to optimize cache utilization. Default is true. Does not apply if splat scene is progressively loaded.
freeIntermediateSplatData When true, the intermediate splat data that is the result of decompressing splat bufffer(s) and used to populate data textures will be freed. This will reduces memory usage, but if that data needs to be modified it will need to be re-populated from the splat buffer(s). Defaults to false.
splatRenderMode Determine which splat rendering mode to enable. Valid values are defined in the SplatRenderMode enum: ThreeD and TwoD. ThreeD is the original/traditional mode and TwoD is the new mode described here: https://surfsplatting.github.io/
sceneFadeInRateMultiplier Customize the speed at which the scene is revealed. Default is 1.0.

Creating KSPLAT files

To convert a .ply or .splat file into the stripped-down and compressed .ksplat format, there are several options. The easiest method is to use the UI in the main demo page at http://127.0.0.1:8080/index.html. If you want to run the conversion programatically, run the following in a browser:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';

const compressionLevel = 1;
const splatAlphaRemovalThreshold = 5; // out of 255
const sphericalHarmonicsDegree = 1;
GaussianSplats3D.PlyLoader.loadFromURL('<path to .ply or .splat file>',
                                        onProgress,
                                        progressiveLoad,
                                        onProgressiveLoadSectionProgress,
                                        minimumAlpha,
                                        compressionLevel,
                                        optimizeSplatData,
                                        sphericalHarmonicsDegree)
.then((splatBuffer) => {
    GaussianSplats3D.KSplatLoader.downloadFile(splatBuffer, 'converted_file.ksplat');
});

Both of the above methods will prompt your browser to automatically start downloading the converted .ksplat file.

The third option is to use the included nodejs script:

node util/create-ksplat.js [path to .PLY or .SPLAT] [output file] [compression level = 0] [alpha removal threshold = 1] [scene center = "0,0,0"] [block size = 5.0] [bucket size = 256] [spherical harmonics level = 0]

For the nodejs script, it may be necessary to increase the heap size for larger scenes. Use the parameter --max-old-space-size=[heap size in MB] to do so:

node util/create-ksplat.js --max-old-space-size=8192 [... remaining arguments]

Currently supported values for compressionLevel are 0, 1, or 2. 0 means no compression and 1 means compression of scale, rotation, position, and spherical harmonics coefficient values from 32-bit to 16-bit. 2 is similar to 1 except spherical harmonics coefficients are compressed to 8-bit.


CORS issues and SharedArrayBuffer

By default, the Viewer class uses shared memory (via a typed array backed by a SharedArrayBufffer) to communicate with the web worker that sorts the splats. This mechanism presents a potential security issue that is outlined here: https://web.dev/articles/cross-origin-isolation-guide. Shared memory can be disabled by passing false for the sharedMemoryForWorkers parameter to the constructor for Viewer, but if you want to leave it enabled, a couple of extra CORS HTTP headers need to be present in the response from the server that is sent when loading the application. Without those headers set, you might see an error like the following in the debug console:

"DOMException: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': SharedArrayBuffer transfer requires self.crossOriginIsolated."

For the local demo I created a simple HTTP server (util/server.js) that sets those headers:

response.setHeader("Cross-Origin-Opener-Policy", "same-origin");
response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");

CORS with Apache

For Apache, you can edit the .htaccess file to allow CORS by adding the lines:

Header add Cross-Origin-Opener-Policy "same-origin"
Header add Cross-Origin-Embedder-Policy "require-corp"

Additionally you may need to require a secure connection to your server by redirecting all access via http:// to https://. In Apache this can be done by updating the .htaccess file with the following lines:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

CORS with Vite

For Vite, one popular option is to install the vite-plugin-cross-origin-isolation plugin via npm and then add the following to your vite.config.js file.

import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    {
      name: "configure-response-headers",
      configureServer: (server) => {
        server.middlewares.use((_req, res, next) => {
          res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
          res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
          next();
        });
      },
    },
  ],
});

There are other ways to configure Vite to handle this referenced in issue #41.

gaussiansplats3d's People

Contributors

mkkellogg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gaussiansplats3d's Issues

error ,need help

npm run build

[email protected] build
npm run build-library && npm run build-demo

[email protected] build-library
npx rollup -c && mkdir -p ./build/demo/lib && cp ./build/gaussian-splat-3d* ./build/demo/lib/

./src/index.js → ./build/gaussian-splat-3d.umd.cjs...
created ./build/gaussian-splat-3d.umd.cjs in 124ms

./src/index.js → ./build/gaussian-splat-3d.module.js...
created ./build/gaussian-splat-3d.module.js in 64ms
命令语法不正确。

Not working on android browsers except firefox android

I have tried the online demo and also set up a website for viewing and configured the proper CORS settings in Nginx, as well as implemented HTTPS. The site is functioning fast and well on Windows browsers and iOS. However, it is not working on the default browser (webview) in Android. I checked the 'crossOriginIsolated' variable using dev tools and found that it is always false, regardless of the CORS settings I apply. Interestingly, the site works well on the Firefox mobile browser on the same device.

Upon consulting MDN, I discovered that while the 'crossOriginIsolated' variable is available in webview, the term 'SharedArrayBuffer' is never supported in webview. Additionally, MDN indicated that Chrome on Android supports SharedArrayBuffer, but it still failed to run on Chrome for Android.

Do you have any ideas on how to resolve this issue and get the website running on Android browsers? A fix would be great help and enable a broader range of users. Thanks.

Local demo fails because of missing splats

When I want to test the demos on my local clone, after having run npm run build there is no folder ./build/demo/assets/data/stump/[*.splat]. Therefore running npm run demo will eventually not open any demo samples.

Not sure if I am doing something wrong, but if not, would there be any chance to include the demo splats in the repo?

Relighting

Any thoughts on how lighting sim could be done in the future strictly inside three js?

Can `Cesium` support displaying `.ksplat` model files?

The compression effect of .ksplat model on .ply is great! I wonder if it is possible to display .ksplat models in Cesium?

For example, displaying a .ksplat model of a building on Cesium using a Cesium plugin, and ensuring compatibility with Cesium's browsing features like zooming, panning, and rotating...

[Help needed] - File loaded, but not displayed

I setup an example here:
https://stackblitz.com/edit/github-ugddhz?file=index.html,src%2Fapp.js

The file is loading, the CORS are working, too. But nevertheless I do not see my file. It is a ply file from poly.cam that I converted with your converter to make it a splat file. But other files are there, too (also in the "local" public folder), and they won't work, either.

Am I doing something wrong?

And is there another place to ask question or can I ask here?

PS: I should add that the converted splat file is displayed correctly on the website demo page, where it is possible to upload a file...

Rendering on Smartphone

Super nice work, it renders super smooth on my Macbook pro :)

I "optimized "the ply to splat with your function but it still doens't render on my android phone (not super fast but not slow Pixel 7)

Any ideas of optimization? I tested the 3 examples too, it doesn"t work.

Thanks!

Typescript support

Hey,

I'd like to hear your stance on this topic.

Personally I think it's a great developer tool and a great aid when exposing the API to the end user.

I'd happily convert the existing code if you'd like me to.

Support more splat compressions levels

Some splat imported from other compressors have different compression levels.
namely splats imported from playcanvas compressor have 190 as compression level and splats from polycam website have 97 as level and in SplatBuffer.js at line 58 it throws an error as it's not able to load these files.
is there any way to fix this behaviour?

I have an idea about a 3D Gaussian viewer.

I have added camera viewpoint movement in the scene, although it may not be suitable for every model. Perhaps if I can obtain the camera poses from the trained model, it would be possible to automatically handle the camera motion trajectory. What do you think?

Splats disappearing depending on tilt

Hi @mkkellogg !

I'm experiecing some strange behaviour and i was wondering if you could help me/had an idea of what could be happening.

I have a ksplat that i'm trying to playback but depending on the tilt of the camera a lot of the splats disappear.
This doesn't happen when using the SIBR viewer (or antimatter15 implementation for what i can tell, but i might be wrong, as i just used the online viewer and is not too user friendly).

For what is worth i think it's to do with the sorting: i removed the toHalfFloat for the covariance and i could see all the points without the splats, and they were still disappering. Then again, i'm not sure why just sorting the splats would make them disappear, so maybe it's something else.

I attacched a test i've done:
converted_file.zip
If you try with:
'cameraUp': [0, 0, -1],
'initialCameraPosition': [9993.60576, -277.27384, -435.28904],
'initialCameraLookAt': [9293.52797, 251.08879, 28.23262],

and slightly tilt up/down you should see them disappearing

Note on the file: when i was playing around with the bucketSize and having huge files back, i did something to cull out the model given a point and a radius.
What i've attacched is just a culled version fo the entire model, i was trying to check if there was something "in front" that would occlude the rest, i can assure you this happens on the full model as well, this is just simplier to attach 😄

Is it possible to invert the vertical rendering of a SplatMesh object?

When using an external scene, the SplatMesh object does not react in any way to changes in the camera's Up vector. All examples in the description use the negative direction of the camera Y axis, but in ThreeJS the positive Y direction is the default. Can you tell me where to change your code in order to either implement a dependence on the Up vector of the camera or at least change the direction of the SplatMesh rendering?

docker port trouble

Thanks for the great development.
When I create an environment with docker container in wsl on windows, if the port number mapped in docker-compose.yml and the port number described in server.js are the same, the server cannot start and does not output any errors. I imagine this is related to something in this library. Do you have any idea what it could be?

For example, in the following example, node server.js will not start, and i must either change the mapping to 8080:8081 or devise a way to change the port specification in server.js to 8082.

version: '3' services: app: container_name: app build: context: . dockerfile: Dockerfile restart: always tty: true volumes: - type: bind source: . target: /app ports: - 8080:8080

Error: export 'default' (imported as 'SorterWasm') was not found in './sorter.wasm'

Hi. Can you tell me what sorter.wasm file is for and how to compile it? I'm trying to build this package in my Angular project. When trying to create a build, the following errors appear:

./src/app/services/engine/GaussianSplatsViewer/worker/SortWorker.js:96:38-48 - Error: export 'default' (imported as 'SorterWasm') was not found in './sorter.wasm' (possible exports: __wasm_call_ctors, emscripten_tls_init, sortIndexes)
./src/app/services/engine/GaussianSplatsViewer/worker/sorter.wasm - Error: Module not found: Error: Can't resolve 'env' in 'E:\Projects\artwork-threed-visualizer\VoyagerFrontend\src\app\services\engine\GaussianSplatsViewer\worker'

I see the compile_wasm.sh script, I ran it using PowerShell but no new file was generated. I also don’t see anything in the package.json file that relates to wasm. Can you tell me what these files are needed for and how to configure the build correctly?
Or is sorter.wasm just a binary file that needs to be imported into a JS file as a string?
EDIT:
It doesn't matter anymore. I used fetch to download this file. And to fix the error with self.crossOriginIsolated, I set some headers in the angular.json file. Maybe this will help someone in the future.

Viewer.js
fetch('assets/libs/sorter.wasm').then((response) => response.arrayBuffer())
                .then((sortWorkerBuffer) => {
                    const base64String = this.arrayBufferToBase64(sortWorkerBuffer);
...
});
angular.json
"serve": {
          "builder": "@angular-builders/custom-webpack:dev-server",
          "options": {
            "browserTarget": "artwork-threed-visualizer:build",
            "headers": {
              "Cross-Origin-Opener-Policy": "same-origin",
              "Cross-Origin-Embedder-Policy": "credentialless"
            }
          },

Convert .ply to .splat

Dear author,
I'm a new user for java. Could you please tell me where to put the code you provide to convert .ply file to .splat file? Thank you.
Best regards,
hhc

The results are different when rotated by dynamicScene and when rotated by addSplatScene.

I tried to rotate 180° along the Z axis in the model direction.
I tried to rotate using addSplatScene with params.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title>3D Gaussian Splat Demo - Garden</title>
  <script type="text/javascript" src="js/util.js"></script>
  <script type="importmap">
    {
        "imports": {
            "three": "./lib/three.module.js",
            "@mkkellogg/gaussian-splats-3d": "./lib/gaussian-splats-3d.module.js"
        }
    }
  </script>
  <style>

    body {
      background-color: #000000;
      height: 100vh;
      margin: 0px;
    }

  </style>

</head>

<body>
  <script type="module">
    import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
    import * as THREE from 'three';

    const quaternion = new THREE.Quaternion();
    const euler = new THREE.Euler( 0.0, 0.0, Math.PI);
    quaternion.setFromEuler(euler);

    const viewer = new GaussianSplats3D.Viewer({
      "dynamicScene": false,
    });
    let path = 'assets/data/garden/garden';
    path += isMobile() ? '.ksplat' : '_high.ksplat';
    viewer.addSplatScene(path,{
      "rotation": quaternion.toArray()
    })
    .then(() => {
        viewer.start();
    });
  </script>
</body>

</html>

I got the result as shown in the following image.

image

Next, I tried to rotate using dynamicScene.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title>3D Gaussian Splat Demo - Garden</title>
  <script type="text/javascript" src="js/util.js"></script>
  <script type="importmap">
    {
        "imports": {
            "three": "./lib/three.module.js",
            "@mkkellogg/gaussian-splats-3d": "./lib/gaussian-splats-3d.module.js"
        }
    }
  </script>
  <style>

    body {
      background-color: #000000;
      height: 100vh;
      margin: 0px;
    }

  </style>

</head>

<body>
  <script type="module">
    import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
    import * as THREE from 'three';

    const quaternion = new THREE.Quaternion();
    const euler = new THREE.Euler( 0.0, 0.0, Math.PI);
    quaternion.setFromEuler(euler);

    const viewer = new GaussianSplats3D.Viewer({
      "dynamicScene": true,
    });
    let path = 'assets/data/garden/garden';
    path += isMobile() ? '.ksplat' : '_high.ksplat';
    viewer.addSplatScene(path)
    .then(() => {
        const splatScene = viewer.getSplatScene(0);
        splatScene.quaternion.copy(quaternion);
        viewer.start();
    });
  </script>
</body>

</html>

I got the result as shown in the following image.

image

I wanted to reproduce the result of dynamicScene with addSplatScene.
How can I reproduce this using addSplatScene? Also, is this behavior the correct behavior for the inconsistency?

Load another Splat resource

I am loading another Splat in the garden scene, which may cause occlusion issues. When I move the camera's perspective, the table will disappear, and I cannot find the reason for it

微信图片_20231102165400
微信图片_20231102165407

Is it possible to modify the 'splatAlphaRemovalThreshold' parameter during the rendering process?

This library is very useful and perfectly meets my needs. I would like to implement a loading animation similar to LumaAI based on this. It seems that they may also control the 'splatAlphaRemovalThreshold' parameter to achieve this, as seen here: https://lumalabs.ai/capture/443a844e-525f-41e1-9750-1dff62782466. I roughly examined the code and it seems that the 'splatAlphaRemovalThreshold' parameter is already passed to the 'loadFileToSplatBuffer' function before rendering, and afterwards, it seems that the parameter cannot be modified to adjust the rendering behavior. Therefore, I would like to know if I have missed anything or if there is any way to achieve this, thanks!

Camera orientation settings not clear

Nicely done ...

Unfortunately, at the moment with the View Your Own Scene on the demo page, it's very unintuitive as to how to set the Camera orientation parameters to get an uploaded splat view to change with the controls as expected - I tried half a dozen times, and always failed. Would it be possible to make this clearer, or easier to use?

[Question] Is there a "common solution" for centring models?

My current workflow is:

  • Create in Luma / poly.cam
  • Download .ply
  • Clean up in Super Splash
  • Convert to .splat with GaussianSplats3D
  • Upload / implement .splat

The issue I encounter is that the orientation of a splat is obviously treated very differently among software. I am aware of the Y/Z-axis, but in this case the splat is not 90 degrees off (which I would expect, when y and z axis were treated differently), but the splat is completely turned around.

The screenshot shows what I have in Super Splash and the video here:
https://dl.rowild.at/GaussianSplats3D_upside_down.mp4
shows what GaussianSplats3D is doing with the (converted from .ply) .splat.

I am aware that the distance of the camera has to be adjusted in most cases. (Of course I would love to know if that is the case, because the .splat is scaled differently or is there really just a difference ein placing the camera.)
But, as mentioned above: the splat is turned upside-down in GaussianSplats3D – and that does not really seem to be the case, because the z-axis is defined differently.

Not sure, if my thinking is totally wrong and splats work completely different from meshes, or if there is something else off...
I know my way around three.js so-lala and would like to think that, along with the documentation, I can re-define the OrbitControls and the camera so that my splat is handled properly. But it seems that currently these adjustments have to be done on a splat-to-splat basis.

want accessible mobile page

Your work is a good one. What I would like to ask is Deploying this project to an accessible web page is accessible on the computer side, but not on the mobile (iPhone is ok) side, and it shows the problem of loading all the time.
Do you know where you need to change your source code?

best regards,

Camera up vs Orientation behavior

Problem description:

Setting camera UP vector on file loading works great for orienting my model. But I would love to use the orientation feature instead so the model can exist in default world space. However, it does not appear to work how I'd expect. I would assume it would yield the same visual effect as changing the camera up i.e. the camera orbits the camera on the correct plane.

Can you confirm the expected behavior/functionality and usage?

Relevant code:

const up = [0.1294521689414978, -0.4815174639225006, -0.8668235540390015]
const viewer = new Viewer({
    rootElement: containerRef.current,
    //cameraUp: up,
    initialCameraPosition: [-1, 4, 6],
    initialCameraLookAt: [0, 0, 0],

});

const orientation = new THREE.Quaternion();
orientation.setFromUnitVectors(
    new THREE.Vector3(0, 1, 0),
    new THREE.Vector3(up[0], up[1], up[2]).normalize()
);

viewer.init();
viewer.loadFile(file, {
    splatAlphaRemovalThreshold: 5,
    halfPrecisionCovariancesOnGPU: true,
    position: [0, 0, 0],
    orientation: orientation.toArray(),
})
    .then(() => {
        console.log('File loaded');
        viewer.start();
    });

Suggested solution:

I dug through the code to look for potential bugs, but came up empty handed. I lose track of the splatMesh.quaternion but my guess is because its a member of a threejs parent class.

Thanks!

Run establish an online demo error

Thanks for your great work !
I have a program

Reproduction Step:

I use the nginx proxy pass port, and add the following configuraions:
add_header 'Cross-Origin-Embedder-Policy' 'require-corp';
add_header 'Cross-Origin-Opener-Policy' 'same-origin';

Acutal Result:

But appear:
DOMException: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': SharedArrayBuffer transfer requires self.crossOriginIsolated .

How can i resolve this program~
Thanks

Demo page not working

Latest changes possibly caused a problem - will happily process a file from local system; but only shows a black screen for any of the demo items.

WebXR support for Quest 3

Hey great work here so far @mkkellogg!

I'd love to help out where we can on a WebXR viewer for Splats.
We have our own internal renderer right now running on WebXR but it's closed source. Ideally we could support a open source renderer in a popular framework like Three.js to make them more accessible.

We have optimized splats to make them really small while retaining most of the quality. We have the splat here in the repo running at 90FPS on Quest 3 in our WebXR renderer. Hope this can be useful for testing out in the headset since most of the data out there is too large to be useful.
https://github.com/gmix-tech/small_splats

Let me know how I can help out.
Feel free to ping me at [email protected]

Not rendering local host

After:

npm run demo

I get:

Server running at 127.0.0.1:8080


HTTP(500)) Request for ./build/demo/ -> Server error.
HTTP(404) Request for ./build/demo/favicon.ico -> File not found.

When I navigate to local host URL it says this:

Sorry, check with the site admin for error: EISDIR ..

When I used SERVEZ running locally I get the index page to load but the links to the three example pages don't load splats.

I have demo data installed here:

GaussianSplats3D-main\build\demo\assets\data

\garden
\stump
\truck

All three directories have associated splat files.

Any ideas what I might be doing wrong?

Discussion: remove transparent splats during data conversion?

I've just written my own renderer:
https://github.com/fynv/web_gaussian_splats
, which is heavily based on the great work here. Thanks very mcuh!

One thing I noticed is that if I don't remove the splats with alpha == 0,
there can be some obvious performance defficiency (fragment shader bound).
The indication is that there are a signicant number of splats with alpha ==0.

The culling is currently done when building the "splat-tree" structure, where "splatAlphaRemovalThreshold" is defaulted to 1.
It seems to me that for alpha == 0, it doesn't even make sense to store these splats in the .splat data in the first place.
Can we just drop these splats during data conversion?

Larger file size of .splat files

Hi,
I was wondering why the file size of .splat files are so high (100+mb) compared to Kevin's .splat files(25mb).
Skimming through the codebase, this implementation also discards the spherical harmonics coefficient. Is there any specific reason for the larger file size ?
Thank you ! :)

Support for disposal and freeing up client resources

In a context of SPA web apps we need a method of the Viewer to release hogged up resources.
The issue is: If the viewer is integrated into a SPA framework (Blazor in my case), upon navigating to a "page" with no viewer and then back again to the viewer, there will be a significant performance and stuttering issues up to a point of unusability. Resources from each load/visit of the viewer are still hogging clients memory. This is because resources aren't freed since a navigation in a SPA means just an in-place html replace and NOT a full page reload. I checked memory usage upon each viewer initialization, and it keeps adding up but not freeing the memory since SPA frameworks dont know how to do that with .js resources. SPAs only handle their component disposal which has nothing to do with resources that are used up by .js libraries. This will eventually result in memory leak.
Usually .js libraries offer some kind of a destroy or dispose method. Is there a way for the viewer to do the same? If not I would grately appreciate some sort of an implementation in the future release.

PR

would be great to PR some of those classes to mrdoob/three.js?

how to select coordinate points in the Splat scene

There is an issue that has been bothering me, which is how to select coordinate points in the Splat scene through mouse clicks in order to conveniently add regular meshes to the scene. However, when I tried to implement it using the raycaster.intersectObjects() method, it didn’t work.

Depth sorting for foreign objects

Is it possible to make depth sorting apply to added 3b objects? For example debug meshes in demo are rendered even when occluded by terrain
image

`.splat` universal format discussion

Hello! I'm the author of gsplat.js, in which I'm using the splat format as provided in antimatter15/splat

I have opened an issue on splat compression, and I think it would be great if we can have a universal representation, with a consistent header to support different compression methods.

I can replicate your compressed format, but maybe we can open a common format repo with test files, so we can stay on the same page?

What do you think?

Offsetting splat

Hello, I am not very familiar with Three.js but have worked with Webgl and was wondering where in the code I might be able to offset the origin to draw the splat in a different location. I presumed it should be in the SplatBuffer where I have tried modifying the position functions to shift the splat. But all I have achieved is some of the guassians being shifted and not all. So I was wondering if you would know off the top of your head where this would take place. I also tried adding a translation matrix buildPreComputedBuffers() and that didn't seem to work. If you could point me in a direction where this would be achievable in your code that would me much appreciated

Set internal render resolution?

Hi, im wondering if theres a way in the code to set the internal render resolution, so maintain the same size viewer but for example set the model itself to be at 0.5* the resolution?

3D Doesn't Appear

Hello. I was looking to work on Gaussian Splat with Three.js and found this GitHub repository.
However, I immediately ran into a major obstacle.
Even after installing and uploading to the server, and testing with the codes provided in the sample, I can't see the ksplat on my server. The splat files we worked on also don't upload.
In the sample page console, there are a few more Views.js files that appear, but
Views.js doesn't show up in our console.
What could be the problem?

we
` import * as THREE from 'three';
import * as GaussianSplats3D from 'gaussian-splats-3d';

const viewer = new GaussianSplats3D.Viewer({
    'cameraUp': [0, -1, -0.54],
    'initialCameraPosition': [-3.15634, -0.16946, -0.51552],
    'initialCameraLookAt': [1.52976, 2.27776, 1.65898]
});
viewer.init();
let path = './shoose.splat';
viewer.loadFile(path, {
    'halfPrecisionCovariancesOnGPU': true
})
.then(() => {
    viewer.start();
});`

error build

npm run build

[email protected] build
npm run build-library && npm run build-demo

[email protected] build-library
npx rollup -c && mkdir -p ./build/demo/lib && cp ./build/gaussian-splat-3d* ./build/demo/lib/

./src/index.js → ./build/gaussian-splat-3d.umd.cjs...
(!) Broken sourcemap
https://rollupjs.org/troubleshooting/#warning-sourcemap-is-likely-to-be-incorrect
Plugins that transform code (such as "base64") should generate accompanying sourcemaps.
created ./build/gaussian-splat-3d.umd.cjs in 131ms

./src/index.js → ./build/gaussian-splat-3d.module.js...
(!) Broken sourcemap
https://rollupjs.org/troubleshooting/#warning-sourcemap-is-likely-to-be-incorrect
Plugins that transform code (such as "base64") should generate accompanying sourcemaps.
created ./build/gaussian-splat-3d.module.js in 70ms
命令语法不正确。

Scale parameter

Hi @mkkellogg !

I was wondering what was your opinion on adding another parameter that can be passed to the viewer and that would tide together MaximumDistanceToSort/MaximumDistanceToRender/SplatBufferBucketBlockSize and the Camera.far?

Is there any drawback?
Did i miss some other variable?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.