gaussian-splat-lite 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -2
- package/README.md +22 -13
- package/dist/gaussian-splat-lite.cjs +700 -51
- package/dist/gaussian-splat-lite.cjs.map +1 -1
- package/dist/gaussian-splat-lite.min.cjs +40 -13
- package/dist/gaussian-splat-lite.min.cjs.map +1 -1
- package/dist/gaussian-splat-lite.module.js +702 -53
- package/dist/gaussian-splat-lite.module.js.map +1 -1
- package/dist/gaussian-splat-lite.module.min.js +2660 -2199
- package/dist/gaussian-splat-lite.module.min.js.map +1 -1
- package/dist/types/addons/TAAPass.d.cts +6 -15
- package/dist/types/addons/TAAPass.d.ts +6 -15
- package/dist/types/capture/SplatCapture.d.cts +66 -0
- package/dist/types/capture/SplatCapture.d.ts +66 -0
- package/dist/types/capture/WebGLCapture.d.cts +13 -0
- package/dist/types/capture/WebGLCapture.d.ts +13 -0
- package/dist/types/capture/WebGPUCapture.d.cts +17 -0
- package/dist/types/capture/WebGPUCapture.d.ts +17 -0
- package/dist/types/capture/pixels.d.cts +5 -0
- package/dist/types/capture/pixels.d.ts +5 -0
- package/dist/types/capture/rendererState.d.cts +3 -0
- package/dist/types/capture/rendererState.d.ts +3 -0
- package/dist/types/capture/sceneView.d.cts +3 -0
- package/dist/types/capture/sceneView.d.ts +3 -0
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/rendering/GaussianSplatRenderer.d.cts +13 -11
- package/dist/types/rendering/GaussianSplatRenderer.d.ts +13 -11
- package/dist/types/rendering/SplatAccumulator.d.cts +2 -1
- package/dist/types/rendering/SplatAccumulator.d.ts +2 -1
- package/dist/types/rendering/backend.d.cts +1 -1
- package/dist/types/rendering/backend.d.ts +1 -1
- package/dist/types/rendering/rendererUtils.d.cts +1 -5
- package/dist/types/rendering/rendererUtils.d.ts +1 -5
- package/dist/types/rendering/tsl/tslCompat.d.cts +1 -0
- package/dist/types/rendering/tsl/tslCompat.d.ts +1 -0
- package/dist/types/rendering/uniforms.d.cts +0 -3
- package/dist/types/rendering/uniforms.d.ts +0 -3
- package/dist/types/scene/SplatMesh.d.cts +1 -1
- package/dist/types/scene/SplatMesh.d.ts +1 -1
- package/docs/Architecture.md +48 -3
- package/docs/GaussianSplatRenderer.md +71 -55
- package/docs/PostDecode.md +52 -36
- package/docs/RadStreamScheduler.md +53 -41
- package/docs/SogStreamScheduler.md +49 -39
- package/docs/SplatCapture.md +147 -0
- package/docs/SplatEdit.md +33 -26
- package/docs/SplatFileType.md +11 -13
- package/docs/SplatLoader.md +19 -14
- package/docs/SplatMesh.md +69 -49
- package/docs/Splats.md +33 -26
- package/docs/StochasticRendering.md +27 -30
- package/docs/TAAPass.md +97 -73
- package/package.json +1 -1
- package/docs/SplatAccumulator.md +0 -37
package/docs/Architecture.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[Back to documentation](../README.md#documentation)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This guide is for contributors. It describes internal module boundaries, backend differences, and resource lifetimes. To use the library, start from the [README](../README.md#quick-start) and the API pages it lists; those pages describe behavior, and this one explains how it is implemented.
|
|
6
|
+
|
|
7
|
+
[`src/index.ts`](../src/index.ts) defines the public exports. Applications use `SplatMesh` for models and one `GaussianSplatRenderer` for the scene.
|
|
6
8
|
|
|
7
9
|
## Source layout
|
|
8
10
|
|
|
@@ -21,11 +23,13 @@
|
|
|
21
23
|
| `rust/gaussian-splat-rs/src/` | WASM bridges, typed-array output, LOD selection, sorting, and raycasting |
|
|
22
24
|
| `src/runtime/` | Worker RPC, pooling, transferables, and WASM initialization |
|
|
23
25
|
| `src/utils/` | Numeric conversion, spatial transforms, and Three.js helpers |
|
|
24
|
-
| `src/rendering/` | Shared updates
|
|
26
|
+
| `src/rendering/` | Shared updates, sorting handoffs, backend selection, and stochastic blue noise |
|
|
25
27
|
| `src/rendering/tsl/` | Shared TSL generation, projection, drawing, and view uniforms |
|
|
26
28
|
| `src/rendering/webgpu/` | Compute projection, projected caches, GPU sorting, and indirect drawing |
|
|
27
29
|
| `src/rendering/webgl/` | GLSL materials, texture generation, and CPU ordering uploads |
|
|
28
30
|
| `src/rendering/webgl-fallback/` | WebGPURenderer WebGL2 raster generation and ordering uploads |
|
|
31
|
+
| `src/capture/` | Optional offscreen targets, readback, cube captures, and PMREM filtering |
|
|
32
|
+
| `src/addons/` | Temporal anti-aliasing: `TAAPass` for WebGLRenderer, `TAANode` for WebGPURenderer, and their shared state |
|
|
29
33
|
|
|
30
34
|
## Rendering boundaries
|
|
31
35
|
|
|
@@ -34,6 +38,7 @@ The renderer combines visible models using Three.js transforms, visibility, and
|
|
|
34
38
|
| Owner | Responsibility |
|
|
35
39
|
| --- | --- |
|
|
36
40
|
| `GaussianSplatRenderer` | Scene updates, accumulator handoff, and sorting |
|
|
41
|
+
| `SplatCapture` | Independent capture Splat state, offscreen targets, readback, and PMREM |
|
|
37
42
|
| `SplatAccumulator` | Scene mappings, versions, camera-relative origin, and WebGL texture generation |
|
|
38
43
|
| WebGPU backend | Compute projection, caches, GPU sorting, and indirect drawing |
|
|
39
44
|
| WebGL backend | GLSL materials, ordering textures, and texture generation |
|
|
@@ -44,11 +49,49 @@ For sorted rendering, native WebGPU keeps ordering and visible counts on the GPU
|
|
|
44
49
|
|
|
45
50
|
In `webgpu/`, `ProjectedSplats.ts` coordinates projection and sorting, `ProjectionCache.ts` owns projected storage, and `RadixSort.ts` owns sort passes. WebXR uses the eyes' mean pose for generation and sorting, with separate projections for each eye. Both WebGL backends share `webgl/OrderingTexture.ts` for allocation and disposal, while keeping their own upload and binding paths.
|
|
46
51
|
|
|
52
|
+
`backend.ts` selects the backend from the renderer type and its public `coordinateSystem`, which distinguishes native WebGPU from WebGL fallback. It chooses the blend space for each output: the working color space by default, `renderer.outputColorSpace` when WebGLRenderer draws to the canvas, and the target texture's color space when it draws to an XR-style target.
|
|
53
|
+
|
|
54
|
+
Projected Splat support is capped at the viewport's short side, in projection space scaled by `focalAdjustment`, in both the TSL projection program and the GLSL vertex shader. The quad and its UV extent shrink by the same ratio, preserving the Gaussian profile.
|
|
55
|
+
|
|
56
|
+
Errors from automatic updates are caught and logged once per message; a draw whose preparation fails is skipped. Explicit `update()` calls rethrow to the caller. `frameCallbacks` is an internal flag for a second renderer on the same scene, so each `SplatMesh.onFrame` runs once per frame.
|
|
57
|
+
|
|
47
58
|
Resource rules:
|
|
48
59
|
|
|
49
60
|
- Wait for outstanding GPU compilation before disposing the sorter; preserve compute nodes when resizing resources or changing source meshes.
|
|
50
61
|
- Keep color conversion and XR output handling with the backend that owns them.
|
|
51
62
|
|
|
63
|
+
### Stochastic rendering
|
|
64
|
+
|
|
65
|
+
Sorted and stochastic draws compile separate shaders without mode branches. A mode change takes effect at the next update boundary, after the current draw; both WebGL backends also wait for the matching accumulator and sort, and `stochasticActive` reports the mode being drawn. Stochastic draws use the opaque list with depth testing and writing.
|
|
66
|
+
|
|
67
|
+
Coverage uses a tileable 32-frame spatiotemporal blue-noise atlas (`blueNoise.ts`, generated by `scripts/generate-blue-noise.js`) on all backends. Each Splat keeps its own spatial offset and temporal phase, so consecutive samples follow the atlas's time axis instead of choosing unrelated offsets. The temporal sequence is optimized for exponential history accumulation and wraps after 32 samples. `stochasticSample` advances once per scene render, and every view in that render shares it.
|
|
68
|
+
|
|
69
|
+
Seeds exist only for stochastic draws: WebGL accumulators omit the seed layer in sorted rendering, and native WebGPU allocates its seed buffer on demand. Front-to-back stochastic ordering uses 16-bit keys. With unsorted stochastic rendering, `shrinkResources()` releases the sort worker and ordering.
|
|
70
|
+
|
|
71
|
+
## Capture boundaries
|
|
72
|
+
|
|
73
|
+
[SplatCapture](SplatCapture.md) is optional; the core renderer allocates no capture resources.
|
|
74
|
+
|
|
75
|
+
- **Independent Splat state.** The helper owns an internal `GaussianSplatRenderer` with `autoUpdate` and stochastic rendering off, sharing the display renderer's timer, loaded Splat data, and model-owned SDF edit textures. Its accumulators, sorting, and projection resources are separate, so display ordering and camera state are untouched. Global and model-local edits apply to captures and follow their latest values. `frameCallbacks` is off, leaving `onFrame` to the display renderer.
|
|
76
|
+
- **Stencil captures.** 2D and cube targets follow the Three.js renderer's stencil setting, with no separate capture option; a 2D target without a depth buffer gets no stencil, since Three.js attaches stencil only with depth. The intermediate target uses a depth-stencil attachment when stencil is enabled. With stencil, native depth is 32-bit float only for a reversed depth buffer or a float output depth texture, and only when the device has `depth32float-stencil8`; otherwise it is 24-bit. The scene's stencil masking applies before copying its color and depth to the output.
|
|
77
|
+
- **Options.** Quality, sorting, and material options are copied from the supplied renderer at the start of each capture. The active Splat material's stencil comparison and write settings are copied as well. Cube captures force radial sorting. The internal renderer takes the capture camera's layer mask.
|
|
78
|
+
- **Two phases.** Preparation awaits `update()` on a filtered scene view (`sceneView.ts`), which excludes hidden models from traversal without changing visibility and collects edits with the original scene's visibility. If the display updates shared models during a pending sort, preparation runs again; appearance-only changes reuse the ordering. The draw is synchronous: other Splat renderers' layer masks are cleared, `hideObjects` are hidden, and the internal renderer joins the scene. `withCaptureState()` saves and restores the render target, cube face, mip level, XR setting, automatic clearing, and MRT. Nothing stays changed across an `await`, and everything is restored when drawing throws.
|
|
79
|
+
- **Ordering.** Captures on one helper are queued and run in call order. The camera or `worldCenter` is copied when the call is made. `renderReadTarget()` and `renderEnvMap()` read or filter inside the same queued task, before a later capture can redraw the target.
|
|
80
|
+
- **Cube captures.** Splat data is prepared at `worldCenter` with a 90° camera, then six faces render with automatic updates off. Both WebGL backends share one radial CPU sort across the faces; native WebGPU projects, compacts, and sorts for each face while drawing. Every capture runs `update()`, whose version checks reuse valid data and ordering, so 1.1.8's `update` option is not offered. Mipmaps are generated after the sixth face. Filtered and unfiltered cubes use separate targets, reallocated when size, clipping planes, or the renderer's stencil setting change.
|
|
81
|
+
- **Color.** `WebGLCapture` renders into an intermediate flagged as an XR-style target, 8-bit unless the output is float, so materials convert to the output color space before blending as on the canvas; a full-screen pass then converts to the target's storage space and copies depth. `WebGPUCapture` renders in the working color space, then applies the renderer's output transform and converts to storage space. On both, float outputs keep their type so values stay unclamped, and the intermediate takes the output's sample count, so multisampling applies to the scene and not to the final pass.
|
|
82
|
+
- **Readback.** `pixels.ts` returns packed RGBA8 with the bottom row first on every backend, removing native WebGPU's 256-byte row padding and flipping its top-left origin. Supersampled targets are box-filtered on the CPU. Cube faces are normalized to the WebGL cube render target layout: native WebGPU stores +X and −X swapped and reads each face rotated 180°.
|
|
83
|
+
- **Ownership.** `dispose()` releases the internal renderer, 2D and cube targets, readback buffers, and the PMREM generator. Each texture from `renderEnvMap()` belongs to the caller; disposing it also releases its PMREM render target.
|
|
84
|
+
|
|
85
|
+
## Temporal anti-aliasing boundaries
|
|
86
|
+
|
|
87
|
+
[TAAPass and TAANode](TAAPass.md) are derived from Three.js TRAA, without a velocity attachment: reprojection uses scene depth and camera motion only, so object motion is not tracked.
|
|
88
|
+
|
|
89
|
+
- `taaShared.ts` owns the state both share: a 32-entry Halton(2, 3) jitter sequence, jittered and unjittered projections, previous-frame matrices, the history index, sizing, and reset. It supports custom projections, scaled rigs, reversed depth, and logarithmic depth.
|
|
90
|
+
- Each implementation captures the scene itself into a half-float target with a depth texture and resolves between two history targets, with no history copies. The resolve applies depth rejection away from edges, neighborhood clipping, and luminance-aware blending.
|
|
91
|
+
- The camera's projection is jittered only during capture. Native Splat projection reads the unjittered matrix through `getTAAProjection()`, so projection and sorting results are reused while only the jitter changes.
|
|
92
|
+
- `TAAPass` flags its capture target as an XR-style target, so Three.js converts materials to `renderer.outputColorSpace` before blending, matching canvas blending. Tone mapping is disabled during capture. The final copy decodes the sRGB transfer into the composer's working-space buffer, the inverse of `OutputPass`, so the two round-trip at any alpha; as the last pass it presents the output-space image unchanged. An output color space change resets history.
|
|
93
|
+
- `TAANode` is a TSL node updated once per render. It captures and accumulates in `ColorManagement.workingColorSpace`, leaving tone mapping and output conversion to `RenderPipeline`. A working color space change resets history.
|
|
94
|
+
|
|
52
95
|
## Decoder boundaries
|
|
53
96
|
|
|
54
97
|
PLY, SPZ, SOG, and RAD decode in `gaussian-splat-lib` and publish through `SplatReceiver`. This core library has no JavaScript or WASM dependency. `gaussian-splat-rs` adapts browser inputs and writes the output into JS typed arrays through `SplatsData`.
|
|
@@ -69,6 +112,7 @@ The [postDecode](PostDecode.md) API builds expressions on the calling thread and
|
|
|
69
112
|
| --- | --- |
|
|
70
113
|
| `program.ts` / `builder.ts` | Public API, program identity, typed expressions, and patch validation |
|
|
71
114
|
| `protocol.ts` | Opcodes, packed layouts, and shared types without builder or runtime dependencies |
|
|
115
|
+
| `optimizer.ts` | Fusion of single-use arithmetic intermediates before compilation |
|
|
72
116
|
| `compiler.ts` | Conditional flow, dependencies, packed instructions, and attribute snapshots |
|
|
73
117
|
| `registers.ts` | Runtime register allocation and values carried between condition stages |
|
|
74
118
|
| `operations.ts` / `runtime.ts` | Packed input reads, block execution, conditions, and output writes |
|
|
@@ -81,7 +125,8 @@ Loaders own transport and parsing; schedulers own selection, fades, publication,
|
|
|
81
125
|
|
|
82
126
|
- `StreamWorkerPool.run()` queues worker leases, assigns task IDs, links cancellation, counts downloads, and balances loading-manager notifications. Each loader has its own pool; format-specific callbacks retain SOG caches or release RAD decoder slots.
|
|
83
127
|
- `IndexedSplats` shares texture storage and selected-index reads between `RadPagedSplats` and `SogRegionSplats`. Slot assignment and scene attachment remain with the format-specific scheduler and batch classes.
|
|
84
|
-
- `
|
|
128
|
+
- `StreamCameras.ts` owns the registered cameras and their resolutions, and expands a WebXR `ArrayCamera` into its eye views once they have a projection and viewport.
|
|
129
|
+
- `streamOptions.ts` owns shared defaults, validation, retry classification, and statistics. `StreamByteBudget` bounds pending copies separately from active loads; ready data has no per-update byte allowance.
|
|
85
130
|
|
|
86
131
|
`splatBudget` controls selected detail, not total memory. Resident-byte estimates exclude pending copies and WASM memory, which are reported separately; resident storage has no byte cap. Pending copies allow 8 MiB per concurrent load (32 MiB by default), enlarged for an indivisible RAD page; one oversized item can proceed alone. This bounds the waiting queue, not the bytes published or uploaded per frame.
|
|
87
132
|
|
|
@@ -2,83 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
[Back to documentation](../README.md#documentation)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Draws every visible `SplatMesh` in a scene. Add one to the scene; a single instance displays all your models.
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
7
|
+
```js
|
|
8
|
+
import { GaussianSplatRenderer } from "gaussian-splat-lite";
|
|
9
|
+
|
|
10
|
+
const splatRenderer = new GaussianSplatRenderer({ renderer });
|
|
11
|
+
scene.add(splatRenderer);
|
|
9
12
|
```
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
With `WebGPURenderer`, call `await renderer.init()` before creating it. Create your Three.js renderer with `antialias: false`: multisampling slows Splats down without improving them.
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
| --- | --- | --- | --- |
|
|
15
|
-
| `renderer` | `WebGPURenderer \| THREE.WebGLRenderer` | Required | Three.js renderer; call `await renderer.init()` first when using WebGPURenderer |
|
|
16
|
-
| `onDirty` | `() => void` | `undefined` | Called when the image needs a redraw |
|
|
17
|
-
| `premultipliedAlpha` | `boolean` | `true` | Use premultiplied alpha for blending |
|
|
18
|
-
| `timer` | `THREE.Timer` | New internal timer | Optional shared timer; update it yourself when supplied |
|
|
19
|
-
| `autoUpdate` | `boolean` | `true` | Update visible Splats when rendering |
|
|
20
|
-
| `preUpdate` | `boolean` | `true` | Update before rendering on WebGL; XR updates follow the render pass. Unused on native WebGPU |
|
|
16
|
+
Every option except `renderer` and `timer` is also a property, so you can change it at any time.
|
|
21
17
|
|
|
22
|
-
##
|
|
18
|
+
## Basic options
|
|
23
19
|
|
|
24
20
|
| Option | Type | Default | Description |
|
|
25
21
|
| --- | --- | --- | --- |
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `clipXY` | `number` | `1.25` | Allow centers slightly outside the view; `1` clips at its edge |
|
|
33
|
-
| `focalAdjustment` | `number` | `2` | Adjust projected size; higher values generally look sharper |
|
|
22
|
+
| `renderer` | `WebGPURenderer \| THREE.WebGLRenderer` | Required | Your Three.js renderer |
|
|
23
|
+
| `onDirty` | `() => void` | `undefined` | Called when the image needs a redraw; see [on-demand rendering](#on-demand-rendering) |
|
|
24
|
+
| `autoUpdate` | `boolean` | `true` | Update Splats automatically on each render. Set to `false` to call `update()` yourself |
|
|
25
|
+
| `preUpdate` | `boolean` | `true` | On WebGL, update before drawing so changes show in the same frame. In WebXR, updates run after drawing. Not used on native WebGPU |
|
|
26
|
+
| `timer` | `THREE.Timer` | New internal timer | Share your own timer with [`onFrame`](SplatMesh.md#scene-integration) animations; update it yourself when supplied |
|
|
27
|
+
| `premultipliedAlpha` | `boolean` | `true` | Blend with premultiplied alpha |
|
|
34
28
|
|
|
35
|
-
##
|
|
29
|
+
## Quality options
|
|
36
30
|
|
|
37
31
|
| Option | Type | Default | Description |
|
|
38
32
|
| --- | --- | --- | --- |
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `depthTest` | `boolean` | `true` | Respect depth from other geometry |
|
|
47
|
-
| `depthWrite` | `boolean` | `false` | Write depth directly; normally leave off for transparent Splats |
|
|
48
|
-
|
|
49
|
-
Native WebGPU sorts before drawing. On WebGL, the current sort order stays in use until a new sort is ready.
|
|
50
|
-
|
|
51
|
-
Automatic update errors are caught and logged once per error message, preventing unhandled promise rejections. If draw preparation fails, that draw is skipped so rendering can continue. Explicit `update()` calls report errors to the caller.
|
|
33
|
+
| `maxStdDev` | `number` | `Math.sqrt(8)` | How far each Splat extends from its center. Lower values trim its soft outer edge and render faster; `Math.sqrt(4)` to `Math.sqrt(9)` looks acceptable |
|
|
34
|
+
| `minPixelRadius` | `number` | `1` | Skip Splats smaller than this radius in screen pixels |
|
|
35
|
+
| `minAlpha` | `number` | `1 / 255` | Hide the parts of a Splat more transparent than this |
|
|
36
|
+
| `preBlurAmount` | `number` | `0.3` | Enlarge and soften Splats. Use `0` for models trained with anti-aliasing |
|
|
37
|
+
| `blurAmount` | `number` | `0` | Soften Splats while adjusting their opacity to compensate. For models trained with anti-aliasing, use `0.3` together with `preBlurAmount: 0` |
|
|
38
|
+
| `clipXY` | `number` | `1.25` | How far outside the view a Splat's center may be before the Splat is skipped; `1` cuts exactly at the edge |
|
|
39
|
+
| `focalAdjustment` | `number` | `2` | Adjust the size of Splats on screen; higher values generally look sharper |
|
|
52
40
|
|
|
53
|
-
|
|
41
|
+
A Splat's radius on screen is limited automatically to the short side of the viewport.
|
|
54
42
|
|
|
55
|
-
|
|
43
|
+
## Sorting and blending options
|
|
56
44
|
|
|
57
|
-
|
|
45
|
+
| Option | Type | Default | Description |
|
|
46
|
+
| --- | --- | --- | --- |
|
|
47
|
+
| `sortRadial` | `boolean` | `false` | Sort by distance from the camera instead of by depth. Depth suits most models; distance is steadier while the camera rotates |
|
|
48
|
+
| `fastSort` | `boolean` | `true` | Faster, lower-precision sorting. Has no effect on stochastic rendering |
|
|
49
|
+
| `minSortIntervalMs` | `number` | `0` | Minimum time between sorts on WebGL. Not used on native WebGPU |
|
|
50
|
+
| `transparent` | `boolean` | `true` | Blend Splats as transparent objects |
|
|
51
|
+
| `encodeLinear` | `boolean` | `undefined` | Convert model colors from sRGB to linear before blending; `false` keeps their sRGB values. Chosen automatically when unset |
|
|
52
|
+
| `depthTest` | `boolean` | `true` | Let other geometry hide the Splats behind it |
|
|
53
|
+
| `depthWrite` | `boolean` | `false` | Write Splat depth. Normally leave off, because most of a Splat is transparent |
|
|
58
54
|
|
|
59
|
-
|
|
55
|
+
On native WebGPU, Splats are sorted before every draw. On WebGL, sorting finishes in the background: after the view changes, the previous order stays on screen until the new one is ready.
|
|
60
56
|
|
|
61
|
-
|
|
57
|
+
## Stochastic rendering options
|
|
62
58
|
|
|
63
|
-
|
|
59
|
+
| Option | Type | Default | Description |
|
|
60
|
+
| --- | --- | --- | --- |
|
|
61
|
+
| `stochastic` | `boolean` | `false` | Use stochastic rendering; smooth its noise with temporal anti-aliasing |
|
|
62
|
+
| `stochasticSort` | `boolean` | `true` | Sort Splats from front to back in stochastic rendering; `false` skips sorting |
|
|
63
|
+
| `autoAdvanceStochasticSample` | `boolean` | `true` | Change the noise pattern on every render, including in WebXR |
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
See [Stochastic rendering](StochasticRendering.md) for setup.
|
|
66
66
|
|
|
67
|
-
##
|
|
67
|
+
## Properties and methods
|
|
68
68
|
|
|
69
69
|
| API | Description |
|
|
70
70
|
| --- | --- |
|
|
71
|
-
| `update({ scene, camera })` |
|
|
72
|
-
| `shrinkResources({ scene, camera })` |
|
|
73
|
-
| `clearSplats()` | Clear the
|
|
71
|
+
| `update({ scene, camera })` | Bring the Splats up to date with the scene and camera; returns a promise. Only needed with `autoUpdate = false` |
|
|
72
|
+
| `shrinkResources({ scene, camera })` | Update, then release memory that is no longer needed, for example after removing large models; returns a promise |
|
|
73
|
+
| `clearSplats()` | Clear the Splats from the display without removing models from the scene |
|
|
74
74
|
| `dispose()` | Release this renderer's resources |
|
|
75
|
-
| `stochasticActive` | Read
|
|
76
|
-
| `stochasticSample` | Noise pattern index
|
|
77
|
-
| `synchronousSort` | Read
|
|
78
|
-
|
|
79
|
-
`premultipliedAlpha`, `transparent`, `depthTest`, and `depthWrite` are also writable properties with the behavior listed above.
|
|
75
|
+
| `stochasticActive` | Read-only. Whether stochastic rendering is currently being drawn; it changes shortly after you set `stochastic` |
|
|
76
|
+
| `stochasticSample` | Noise pattern index, `0` by default. Set `autoAdvanceStochasticSample = false` to control it yourself |
|
|
77
|
+
| `synchronousSort` | Read-only. `true` when Splats are always sorted before they are drawn, as on native WebGPU |
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
To update manually after the scene or camera changes:
|
|
82
80
|
|
|
83
81
|
```js
|
|
84
82
|
splatRenderer.autoUpdate = false;
|
|
@@ -86,9 +84,25 @@ await splatRenderer.update({ scene, camera });
|
|
|
86
84
|
renderer.render(scene, camera);
|
|
87
85
|
```
|
|
88
86
|
|
|
87
|
+
Errors during automatic updates are logged to the console, once per message, and rendering continues. Errors from your own `update()` calls are thrown to you.
|
|
88
|
+
|
|
89
|
+
## WebXR and multiple views
|
|
90
|
+
|
|
91
|
+
WebXR needs no extra setup: render with your application camera as usual. A Splat model on either eye's layers is visible in both eyes.
|
|
92
|
+
|
|
93
|
+
For several views outside WebXR, render each one with its own camera. `ArrayCamera` is supported only in WebXR: elsewhere `update()` throws, and automatic draws skip the Splats and log an error. If each view needs its own sort order, give each camera its own Splat renderer on a separate layer.
|
|
94
|
+
|
|
95
|
+
## Color and postprocessing
|
|
96
|
+
|
|
97
|
+
Model colors are converted for you. Control the final image with your Three.js renderer's output color space, as for any other scene.
|
|
98
|
+
|
|
99
|
+
Set `encodeLinear` to choose the conversion yourself. For example, to blend Splats in sRGB next to a linear scene, render them into their own target with `encodeLinear: false`, then unpremultiply that layer, convert it to linear, and premultiply it again before compositing.
|
|
100
|
+
|
|
101
|
+
For temporal anti-aliasing and effects such as Bloom, see [TAAPass and TAANode](TAAPass.md). To render offscreen, read pixels back, or build cube and environment maps, use [SplatCapture](SplatCapture.md).
|
|
102
|
+
|
|
89
103
|
## On-demand rendering
|
|
90
104
|
|
|
91
|
-
|
|
105
|
+
To render only when something changes, connect `onDirty` and your controls to the same render request. This example assumes `renderer`, `scene`, and `camera` already exist:
|
|
92
106
|
|
|
93
107
|
```js
|
|
94
108
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
@@ -124,12 +138,14 @@ const splat = new SplatMesh({ url: "/assets/model.spz" });
|
|
|
124
138
|
scene.add(splat);
|
|
125
139
|
requestRender();
|
|
126
140
|
await splat.initialized;
|
|
127
|
-
requestRender(); // Loading may finish after the
|
|
141
|
+
requestRender(); // Loading may finish after the last requested render.
|
|
128
142
|
```
|
|
129
143
|
|
|
144
|
+
Request a render yourself after changing a model:
|
|
145
|
+
|
|
130
146
|
```js
|
|
131
147
|
splat.opacity = 0.5;
|
|
132
148
|
requestRender();
|
|
133
149
|
```
|
|
134
150
|
|
|
135
|
-
See the [
|
|
151
|
+
See the [quick start](../README.md#quick-start) for a complete setup.
|
package/docs/PostDecode.md
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[Back to documentation](../README.md#documentation)
|
|
4
4
|
|
|
5
|
-
Changes
|
|
6
|
-
|
|
7
|
-
`define()` runs its callback immediately, so mistakes in the expression throw right away; the result is then applied to each Splat while loading.
|
|
5
|
+
Changes every Splat of a model while it loads: move it, scale it, recolor it, or adjust its opacity. Describe the change once with `postDecode.define()`, then pass the result as the `postDecode` option of `SplatMesh` or `Splats`. It works for PLY, SPZ, SOG, and RAD files.
|
|
8
6
|
|
|
9
7
|
```ts
|
|
10
8
|
import { postDecode, SplatFileType, SplatMesh } from "gaussian-splat-lite";
|
|
@@ -22,38 +20,55 @@ const mesh = new SplatMesh({
|
|
|
22
20
|
await mesh.initialized;
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
This example shifts the model and gives it a warmer tint.
|
|
24
|
+
|
|
25
|
+
Your callback runs once, right away, to record the calculation; it does not run for each Splat. That has two consequences:
|
|
26
|
+
|
|
27
|
+
- Build the calculation with the `op` functions. A JavaScript `if` or `+` cannot see individual Splats.
|
|
28
|
+
- Mistakes in the calculation throw immediately from `define()`, not later during loading.
|
|
29
|
+
|
|
30
|
+
A transform never adds, removes, or reorders Splats. The [reordering done by loading](Splats.md#data-rules) happens afterwards.
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
To change a model after it has loaded, use [SplatMesh properties](SplatMesh.md#properties) or [SDF edits](SplatEdit.md) instead.
|
|
33
|
+
|
|
34
|
+
## Splat fields
|
|
35
|
+
|
|
36
|
+
Read these from `splat`:
|
|
28
37
|
|
|
29
38
|
| Field | Type | Description |
|
|
30
39
|
| --- | --- | --- |
|
|
31
|
-
| `splat.position` | `vec3` | xyz
|
|
32
|
-
| `splat.scale` | `vec3` |
|
|
33
|
-
| `splat.quaternion` | `quaternion` | xyzw
|
|
34
|
-
| `splat.opacity` | `float` |
|
|
35
|
-
| `splat.alpha` | `float` |
|
|
40
|
+
| `splat.position` | `vec3` | Center, xyz |
|
|
41
|
+
| `splat.scale` | `vec3` | Size along each axis |
|
|
42
|
+
| `splat.quaternion` | `quaternion` | Rotation, xyzw |
|
|
43
|
+
| `splat.opacity` | `float` | Opacity as stored in the model, which can be above 1 |
|
|
44
|
+
| `splat.alpha` | `float` | Opacity from 0 (transparent) to 1 (opaque) |
|
|
36
45
|
| `splat.color` | `vec3` | RGB color |
|
|
37
|
-
| `splat.sh.coefficient(index)` | `vec3` | One of the 15
|
|
46
|
+
| `splat.sh.coefficient(index)` | `vec3` | One of the 15 view-dependent color coefficients |
|
|
38
47
|
|
|
39
|
-
`splat.sh.map((coefficient, { degree }) => ...)` builds
|
|
48
|
+
`splat.sh.map((coefficient, { degree }) => ...)` builds a calculation for all 15 coefficients at once. Only the ones the model has are updated.
|
|
40
49
|
|
|
41
|
-
##
|
|
50
|
+
## Returning changes
|
|
42
51
|
|
|
43
|
-
Return any
|
|
52
|
+
Return any of `position`, `scale`, `quaternion`, `opacity`, `alpha`, `color`, or `sh`. Fields you leave out stay as they are.
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
- `alpha` is clamped to `[0, 1]`, preserving other opacity state. Output either `opacity` or `alpha`, never both.
|
|
47
|
-
- Quaternions are normalized. Invalid or zero-length results preserve the original.
|
|
48
|
-
- Position and scale updates keep sorting centers synchronized.
|
|
54
|
+
Add `when` to change only the Splats that match a condition. Splats that do not match stay exactly as loaded:
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
```ts
|
|
57
|
+
const transform = postDecode.define(({ splat, op }) => ({
|
|
58
|
+
when: op.lt(op.component(splat.position, 1), 0), // Only Splats below y = 0.
|
|
59
|
+
alpha: 0,
|
|
60
|
+
}));
|
|
61
|
+
```
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
Rules for the values you return:
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
- Return `opacity` or `alpha`, never both. `alpha` is limited to 0–1. A negative `opacity` becomes 0, and values above 1 are allowed up to a fixed maximum.
|
|
66
|
+
- Quaternions are normalized for you. An invalid or zero-length result keeps the original rotation.
|
|
67
|
+
- Values are stored in a compact format, so what you read back later can differ slightly from what you calculated.
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
## External data
|
|
70
|
+
|
|
71
|
+
Inside `define(({ splat, op, attribute }) => ...)`, use `attribute()` to bring in your own per-Splat data, such as a weight or label for each Splat:
|
|
57
72
|
|
|
58
73
|
```ts
|
|
59
74
|
const weights = attribute({
|
|
@@ -67,21 +82,22 @@ const weights = attribute({
|
|
|
67
82
|
```
|
|
68
83
|
|
|
69
84
|
- `data` accepts any `ArrayBufferView`, including `DataView`; `byteOffset` is relative to that view.
|
|
70
|
-
- `components`
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
## Expression operations
|
|
75
|
-
|
|
76
|
-
Use `op` to calculate per-Splat values; JavaScript `if` does not evaluate individual Splats.
|
|
85
|
+
- `components` can be 1 to 4. Formats: `f32`, `f16`, `u8`, `unorm8`, `i8`, `snorm8`, `u16`, `unorm16`, `i16`, `snorm16`, `u32`, `i32`.
|
|
86
|
+
- Order the data like the Splats in the original file, for RAD files too. If it is shorter than the model, only the Splats it covers are changed.
|
|
87
|
+
- The data is read when a load starts. Changing it afterwards affects only later loads.
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
- predicates: `isFinite`, `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `and`, `or`, `not`, and `select`;
|
|
80
|
-
- vectors: `vec2`, `vec3`, `vec4`, `component`, `length`, `normalize`, `dot`, `cross`, and `maxComponentIndex`;
|
|
81
|
-
- rotations: `quaternion`, `quatMul`, and `rotateVector`;
|
|
89
|
+
## Operations
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
- **Arithmetic:** `add`, `sub`, `mul`, `div`, `min`, `max`, `pow`, `clamp`, `mix`, `neg`, `abs`, `sqrt`, `log`, `exp`, `floor`, `ceil`, `round`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`
|
|
92
|
+
- **Conditions:** `isFinite`, `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `and`, `or`, `not`, `select`
|
|
93
|
+
- **Vectors:** `vec2`, `vec3`, `vec4`, `component`, `length`, `normalize`, `dot`, `cross`, `maxComponentIndex`
|
|
94
|
+
- **Rotations:** `quaternion`, `quatMul`, `rotateVector`
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
Things to know:
|
|
86
97
|
|
|
87
|
-
|
|
98
|
+
- A number can be combined with a vector: `op.mul(splat.scale, 2)`.
|
|
99
|
+
- Comparisons and vector products need matching types. `eq` and `ne` compare two values of the same type; `lt`, `lte`, `gt`, and `gte` compare numbers; `dot` takes two vectors of the same size; `cross` takes two `vec3` values.
|
|
100
|
+
- `op.or(A, op.and(B, C))` means `A || (B && C)`. Conditions stop as soon as the answer is known, so put cheap conditions that rule out many Splats first.
|
|
101
|
+
- Values from different `define()` calls cannot be combined.
|
|
102
|
+
- Calculations use 32-bit floats.
|
|
103
|
+
- Very large calculations throw: the limit is 4096 operations.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[Back to documentation](../README.md#documentation)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Shows large RAD scenes by loading only the detail the camera needs, and updating it as the camera moves. It supports RAD version 1 files with levels of detail (LOD), either as a single `.rad` file or split into a header and `.radc` pages. It works on WebGPU and WebGL2, and needs no Spark runtime.
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
8
|
import { RadStreamScheduler } from "gaussian-splat-lite";
|
|
@@ -21,7 +21,7 @@ renderer.setAnimationLoop(() => {
|
|
|
21
21
|
renderer.render(scene, camera);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// Resolves when the first data is on screen. update() must be running.
|
|
25
25
|
await streaming.firstRenderable;
|
|
26
26
|
|
|
27
27
|
// When removing the model:
|
|
@@ -29,11 +29,23 @@ await streaming.firstRenderable;
|
|
|
29
29
|
// streaming.group.removeFromParent();
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
The three steps that matter:
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
1. Add `streaming.group` to the scene. Move, rotate, scale, or hide the scene through this group.
|
|
35
|
+
2. Register the camera and its resolution.
|
|
36
|
+
3. Call `streaming.update()` before every render.
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
Streamed models support [SDF edits](SplatEdit.md) and [picking](SplatMesh.md#raycasting). Their data is read-only.
|
|
39
|
+
|
|
40
|
+
## Cameras and resolution
|
|
41
|
+
|
|
42
|
+
Detail depends on how large the scene appears, so each camera needs a resolution. It is in CSS pixels, without the device pixel ratio, so a scene loads the same detail on standard and high-DPI displays. Set it again whenever the canvas is resized.
|
|
43
|
+
|
|
44
|
+
Register several cameras to load detail for all of them. They share one selection, at the highest detail any of them needs.
|
|
45
|
+
|
|
46
|
+
### WebXR
|
|
47
|
+
|
|
48
|
+
Register `renderer.xr.getCamera()` in place of your camera. Detail is chosen for each eye at the headset's resolution, so no resolution is needed:
|
|
37
49
|
|
|
38
50
|
```js
|
|
39
51
|
const xrCamera = renderer.xr.getCamera();
|
|
@@ -53,50 +65,48 @@ renderer.setAnimationLoop(() => {
|
|
|
53
65
|
});
|
|
54
66
|
```
|
|
55
67
|
|
|
56
|
-
Ordinary camera matrices are updated by the scheduler. The XR camera's matrices are read directly from Three.js, so detail selection before rendering may use the previous frame's pose.
|
|
57
|
-
|
|
58
|
-
For original Splat IDs, use the picking hit's [`sourceIndex`](SplatMesh.md#raycasting).
|
|
59
|
-
|
|
60
68
|
## Options
|
|
61
69
|
|
|
62
70
|
| Option | Default | Description |
|
|
63
71
|
| --- | --- | --- |
|
|
64
72
|
| `url` / `file` / `fileBytes` | Exactly one required | URL, `Blob`/`File`, or complete `Uint8Array`/`ArrayBuffer` |
|
|
65
|
-
| `resolveFile` | Relative
|
|
66
|
-
| `group` | New `THREE.Group` |
|
|
67
|
-
| `splatBudget` | `3_000_000` | Maximum
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `maxConcurrentLoads` | `4` | Maximum
|
|
71
|
-
| `manager` | `THREE.DefaultLoadingManager` |
|
|
72
|
-
| `requestHeader` / `withCredentials` | `{}` / `false` | Request settings
|
|
73
|
-
| `onChange` | — |
|
|
74
|
-
| `onError` |
|
|
73
|
+
| `resolveFile` | Relative URLs | Supply the `.radc` pages of a split scene yourself: `(filename, signal) => URL \| Blob \| Uint8Array \| ArrayBuffer`, directly or as a promise |
|
|
74
|
+
| `group` | New `THREE.Group` | Group that holds the scene |
|
|
75
|
+
| `splatBudget` | `3_000_000` | Maximum number of Splats shown at once. Higher values show more detail and use more memory |
|
|
76
|
+
| `fadeDurationMs` | `200` | Duration of the fade when the scene first appears and when detail changes; `0` switches instantly |
|
|
77
|
+
| `cooldownMs` | `2000` | How long detail that is no longer shown stays in memory, ready for reuse; `0` releases it at once |
|
|
78
|
+
| `maxConcurrentLoads` | `4` | Maximum number of loads in progress at the same time |
|
|
79
|
+
| `manager` | `THREE.DefaultLoadingManager` | Three.js loading manager, including its URL modifiers |
|
|
80
|
+
| `requestHeader` / `withCredentials` | `{}` / `false` | Request settings. Sensitive settings are not sent to other origins |
|
|
81
|
+
| `onChange` | — | Called when the scene needs a redraw |
|
|
82
|
+
| `onError` | Logs to the console | Called when loading fails: `(error, url)` |
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
Detail that is already on screen stays there while its replacement loads. During a fade, old and new detail overlap, so the scene can briefly exceed `splatBudget`; use `fadeDurationMs: 0` to prevent this. The budget limits what is shown, not total memory: detail kept for reuse takes memory too.
|
|
77
85
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
## Common properties and methods
|
|
86
|
+
## Properties and methods
|
|
81
87
|
|
|
82
88
|
| API | Description |
|
|
83
89
|
| --- | --- |
|
|
84
|
-
| `group` |
|
|
85
|
-
| `splatBudget` |
|
|
86
|
-
| `initialized` | Resolves when scene
|
|
87
|
-
| `firstRenderable` | Resolves when the first data
|
|
88
|
-
| `setCamera(camera)` / `deleteCamera(camera)` | Add or remove a camera that
|
|
89
|
-
| `setResolution(camera, width, height)` | Set a registered camera's
|
|
90
|
-
| `setResolutionFromRenderer(camera, renderer)` | Set a registered camera's
|
|
91
|
-
| `update()` |
|
|
92
|
-
| `getBoundingBox()` | Approximate
|
|
93
|
-
| `getGlobalIndex(mesh, renderedIndex)` | Convert a picking
|
|
94
|
-
| `stats` |
|
|
95
|
-
| `dispose()` |
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
| `group` | Group that holds the scene |
|
|
91
|
+
| `splatBudget` | Change it at any time to adjust detail, even while the camera is still. Must be a positive integer |
|
|
92
|
+
| `initialized` | Resolves when the scene is set up; rejects for invalid or unsupported files |
|
|
93
|
+
| `firstRenderable` | Resolves when the first data is on screen, or the scene is empty; rejects if the first data cannot be loaded. Keep calling `update()` while you wait |
|
|
94
|
+
| `setCamera(camera)` / `deleteCamera(camera)` | Add or remove a camera that chooses detail. `hasCamera(camera)` and `cameras` report the registered ones |
|
|
95
|
+
| `setResolution(camera, width, height)` | Set a registered camera's size in CSS pixels; also accepts a `THREE.Vector2` |
|
|
96
|
+
| `setResolutionFromRenderer(camera, renderer)` | Set a registered camera's size from `renderer.getSize()` |
|
|
97
|
+
| `update()` | Advance detail selection, loading, and fades. Call it before every render; it throws if no camera is registered |
|
|
98
|
+
| `getBoundingBox()` | Approximate box around the scene, in the group's coordinates; empty until the first data loads |
|
|
99
|
+
| `getGlobalIndex(mesh, renderedIndex)` | Convert a picking hit into a node index that stays the same for the whole RAD file |
|
|
100
|
+
| `stats` | Numbers for monitoring, such as `visibleSplats`, `loadingChunks`, `downloadedBytes`, and `residentBytes`. Memory figures are estimates, not total browser memory |
|
|
101
|
+
| `dispose()` | Stop loading and release the scene. Pending `initialized` and `firstRenderable` promises reject with `AbortError` |
|
|
102
|
+
|
|
103
|
+
### On-demand rendering
|
|
104
|
+
|
|
105
|
+
Request a redraw from `onChange`, and keep calling `update()` on every animation tick, also when you skip the render. Loading, retries, and fades only make progress inside `update()`.
|
|
106
|
+
|
|
107
|
+
### Picking
|
|
108
|
+
|
|
109
|
+
Hits from a streamed scene refer to the detail that is currently shown. Use `getGlobalIndex()`, or the hit's [`sourceIndex`](SplatMesh.md#raycasting), for an ID that does not change:
|
|
100
110
|
|
|
101
111
|
```js
|
|
102
112
|
const hits = raycaster.intersectObject(streaming.group, true);
|
|
@@ -108,6 +118,8 @@ if (hits.length && hits[0].index !== undefined) {
|
|
|
108
118
|
|
|
109
119
|
## Error recovery
|
|
110
120
|
|
|
111
|
-
Temporary download failures
|
|
121
|
+
Temporary download failures are retried automatically, waiting longer after each attempt, up to 30 seconds. This covers network errors and HTTP 408, 425, 429, and 5xx responses. Each retry calls `onChange`, so an on-demand render loop resumes loading when the connection returns.
|
|
122
|
+
|
|
123
|
+
Invalid data, out-of-memory errors, and other HTTP errors are reported to `onError` and are not retried.
|
|
112
124
|
|
|
113
|
-
If
|
|
125
|
+
If the scheduler can no longer choose detail after an internal failure, it calls `onError` and keeps showing its current detail without refining further. Create a new scheduler to try again.
|