Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/docs/packed-splats.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PackedSplats

A `PackedSplats` is a collection of Gaussian splats, packed into a format that takes exactly 16 bytes per splat to maximize memory and cache efficiency. The `center` xyz coordinates are encoded as float16 (3 x 2 bytes), `scale` xyz as 3 x uint8 that encode a log scale from e^-9 to e^9, `rgba` as 4 x uint8, and quaternion encoded via axis+angle using 2 x uint8 for octahedral encoding of the axis direction and a uint8 to encode rotation amount from 0..Pi.
A `PackedSplats` is a collection of Gaussian splats, packed into a format that takes exactly 16 bytes per splat to maximize memory and cache efficiency. The `center` xyz coordinates are encoded as float16 (3 x 2 bytes), `scale` xyz as 3 x uint8 that encode a log scale from e^-12 to e^9, `rgba` as 4 x uint8, and quaternion encoded via axis+angle using 2 x uint8 for octahedral encoding of the axis direction and a uint8 to encode rotation amount from 0..Pi.

## Creating a `PackedSplats`

Expand Down Expand Up @@ -115,7 +115,7 @@ The center x/y/z components are encoded as float16, which provides 10 bits of ma

### Splat scales encoding

The XYZ scales are encoded independently using the following mapping: Any scale values below e^-20 are interpreted as "true zero" scale, and encoded as `uint8(0)`. Any other values quantized by computing `ln(scale_xyz)`, mapping the range e^-9..e^9 to uint8 values 1..255, rounding, and clamping. This logarithmic scale range can encode values from 0.0001 up to 8K in scale, with approximately 7% steps between discrete sizes, and has minimal impact on perceptible visual quality.
The XYZ scales are encoded independently using the following mapping: Any scale values below e^-30 are interpreted as "true zero" scale, and encoded as `uint8(0)`. Any other values quantized by computing `ln(scale_xyz)`, mapping the range e^-12..e^9 to uint8 values 1..255, rounding, and clamping. This logarithmic scale range can encode values from 0.0001 up to 8K in scale, with approximately 7% steps between discrete sizes, and has minimal impact on perceptible visual quality.

### Splat orientation encoding

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/spark-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const spark = new SparkRenderer({
| **preUpdate** | Controls whether to update the splats before or after rendering. For WebXR this *must* be false in order to complete rendering as soon as possible. (default: `false`)
| **originDistance** | Distance threshold for `SparkRenderer` movement triggering a splat update at the new origin. (default: `1.0`) This can be useful when your `SparkRenderer` is a child of your camera and you want to retain high precision coordinates near the camera.
| **maxStdDev** | Maximum standard deviations from the center to render Gaussians. Values `Math.sqrt(5)`..`Math.sqrt(9)` produce good results and can be tweaked for performance. (default: `Math.sqrt(8)`)
| **enable2DGS** | Enable 2D Gaussian splatting rendering ability. When this mode is enabled, any `scale` x/y/z component that is exactly `0` (minimum quantized value) results in the other two non-zero axes being interpreted as an oriented 2D Gaussian Splat instead of the usual approximate projected 3DGS Z-slice. When reading PLY files, scale values less than e^-20 will be interpreted as `0`. (default: `false`)
| **enable2DGS** | Enable 2D Gaussian splatting rendering ability. When this mode is enabled, any `scale` x/y/z component that is exactly `0` (minimum quantized value) results in the other two non-zero axes being interpreted as an oriented 2D Gaussian Splat instead of the usual approximate projected 3DGS Z-slice. When reading PLY files, scale values less than e^-30 will be interpreted as `0`. (default: `false`)
| **preBlurAmount** | Scalar value to add to 2D splat covariance diagonal, effectively blurring + enlarging splats. In scenes trained without the splat anti-aliasing tweak this value was typically 0.3, but with anti-aliasing it is 0.0 (default: `0.0`)
| **blurAmount** | Scalar value to add to 2D splat covariance diagonal, with opacity adjustment to correctly account for "blurring" when anti-aliasing. Typically 0.3 (equivalent to approx 0.5 pixel radius) in scenes trained with anti-aliasing.
| **focalDistance** | Depth-of-field distance to focal plane (default: `0.0`)
Expand Down
7 changes: 5 additions & 2 deletions examples/editor/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
orbit: false,
reversePointerDir: false,
reversePointerSlide: false,
backgroundColor: "#000000",
openFiles: () => {
fileInput.click();
},
Expand Down Expand Up @@ -485,8 +486,10 @@
spark.focalDistance = Math.exp(value);
});
gui.add(spark, "apertureAngle", 0, 0.01 * Math.PI, 0.001).name("Aperture angle").listen();
scene.background = new THREE.Color(0, 0, 0);
gui.addColor(scene, "background").name("Background color").listen();
scene.background = new THREE.Color(guiOptions.backgroundColor);
gui.addColor(guiOptions, "backgroundColor").name("Background color").onChange((value) => {
scene.background.set(value);
});

const debugFolder = gui.addFolder("Debug").close();
const normalColor = dyno.dynoBool(false);
Expand Down
2 changes: 1 addition & 1 deletion rust/spark-internal-rs/src/raycast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use half::f16;

const MIN_OPACITY: f32 = 0.1;

pub const LN_SCALE_MIN: f32 = -9.0;
pub const LN_SCALE_MIN: f32 = -12.0;
pub const LN_SCALE_MAX: f32 = 9.0;
pub const LN_RESCALE: f32 = (LN_SCALE_MAX - LN_SCALE_MIN) / 254.0; // 1..=255

Expand Down
2 changes: 1 addition & 1 deletion src/PackedSplats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export type PackedSplatsOptions = {
// A PackedSplats is a collection of Gaussian splats, packed into a format that
// takes exactly 16 bytes per Gsplat to maximize memory and cache efficiency.
// The center xyz coordinates are encoded as float16 (3 x 2 bytes), scale xyz
// as 3 x uint8 that encode a log scale from e^-9 to e^9, rgba as 4 x uint8,
// as 3 x uint8 that encode a log scale from e^-12 to e^9, rgba as 4 x uint8,
// and quaternion encoded via axis+angle using 2 x uint8 for octahedral encoding
// of the axis direction and a uint8 to encode rotation amount from 0..Pi.

Expand Down
2 changes: 1 addition & 1 deletion src/SparkRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export type SparkRendererOptions = {
// any scale x/y/z component that is exactly 0 (minimum quantized value) results
// in the other two non-0 axis being interpreted as an oriented 2D Gaussian Splat,
// rather instead of the usual projected 3DGS Z-slice. When reading PLY files,
// scale values less than e^-20 will be interpreted as 0. (default: false)
// scale values less than e^-30 will be interpreted as 0. (default: false)
enable2DGS?: boolean;
// Scalar value to add to 2D splat covariance diagonal, effectively blurring +
// enlarging splats. In scenes trained without the Gsplat anti-aliasing tweak
Expand Down
5 changes: 4 additions & 1 deletion src/defines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
// If these values are changed, the corresponding values in splatDefines.glsl
// must also be updated to match.

export const LN_SCALE_MIN = -9.0;
export const LN_SCALE_MIN = -12.0;
export const LN_SCALE_MAX = 9.0;
export const LN_RESCALE = (LN_SCALE_MAX - LN_SCALE_MIN) / 254.0; // 1..=255
export const SCALE_MIN = Math.exp(LN_SCALE_MIN);
export const SCALE_MAX = Math.exp(LN_SCALE_MAX);

export const LN_SCALE_ZERO = -30.0;
export const SCALE_ZERO = Math.exp(LN_SCALE_ZERO);

// Gsplats are stored in textures that are 2^11 x 2^11 x up to 2^11
// Most WebGL2 implementations support 2D textures up to 2^12 x 2^12 (max 16M Gsplats)
// 2D array textures and 3D textures up to 2^11 x 2^11 x 2^11 (max 8G Gsplats),
Expand Down
2 changes: 1 addition & 1 deletion src/shaders/splatDefines.glsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const float LN_SCALE_MIN = -9.0;
const float LN_SCALE_MIN = -12.0;
const float LN_SCALE_MAX = 9.0;
const float LN_RESCALE = (LN_SCALE_MAX - LN_SCALE_MIN) / 254.0; // 1..=255

Expand Down
25 changes: 13 additions & 12 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as THREE from "three";
import {
LN_RESCALE,
LN_SCALE_MIN,
SCALE_ZERO,
SPLAT_TEX_HEIGHT,
SPLAT_TEX_MIN_HEIGHT,
SPLAT_TEX_WIDTH,
Expand Down Expand Up @@ -370,32 +371,32 @@ export function setPackedSplat(

// Allow scales below LN_SCALE_MIN to be encoded as 0, which signifies a 2DGS
const uScaleX =
scaleX === 0.0
scaleX < SCALE_ZERO
? 0
: Math.min(
255,
Math.max(
0,
1,
Math.round((Math.log(scaleX) - LN_SCALE_MIN) / LN_RESCALE) + 1,
),
);
const uScaleY =
scaleY === 0.0
scaleY < SCALE_ZERO
? 0
: Math.min(
255,
Math.max(
0,
1,
Math.round((Math.log(scaleY) - LN_SCALE_MIN) / LN_RESCALE) + 1,
),
);
const uScaleZ =
scaleZ === 0.0
scaleZ < SCALE_ZERO
? 0
: Math.min(
255,
Math.max(
0,
1,
Math.round((Math.log(scaleZ) - LN_SCALE_MIN) / LN_RESCALE) + 1,
),
);
Expand Down Expand Up @@ -441,32 +442,32 @@ export function setPackedSplatScales(
) {
// Allow scales below LN_SCALE_MIN to be encoded as 0, which signifies a 2DGS
const uScaleX =
scaleX === 0.0
scaleX < SCALE_ZERO
? 0
: Math.min(
255,
Math.max(
0,
1,
Math.round((Math.log(scaleX) - LN_SCALE_MIN) / LN_RESCALE) + 1,
),
);
const uScaleY =
scaleY === 0.0
scaleY < SCALE_ZERO
? 0
: Math.min(
255,
Math.max(
0,
1,
Math.round((Math.log(scaleY) - LN_SCALE_MIN) / LN_RESCALE) + 1,
),
);
const uScaleZ =
scaleZ === 0.0
scaleZ < SCALE_ZERO
? 0
: Math.min(
255,
Math.max(
0,
1,
Math.round((Math.log(scaleZ) - LN_SCALE_MIN) / LN_RESCALE) + 1,
),
);
Expand Down
10 changes: 4 additions & 6 deletions src/worker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import init_wasm, { sort_splats } from "spark-internal-rs";
import type { PcSogsJson, TranscodeSpzInput } from "./SplatLoader";
import { unpackAntiSplat } from "./antisplat";
import { SCALE_MIN, WASM_SPLAT_SORT } from "./defines";
import { WASM_SPLAT_SORT } from "./defines";
import { unpackKsplat } from "./ksplat";
import { unpackPcSogs, unpackPcSogsZip } from "./pcsogs";
import { PlyReader } from "./ply";
Expand Down Expand Up @@ -193,8 +193,6 @@ async function unpackPly({
const numSplats = ply.numSplats;

const extra: Record<string, unknown> = {};
// Anything below this is considered zero and can be rendered as 2DGS
const ZERO_CUTOFF = Math.exp(-20);

ply.parseSplats(
(
Expand All @@ -220,9 +218,9 @@ async function unpackPly({
x,
y,
z,
scaleX < ZERO_CUTOFF ? 0 : Math.max(SCALE_MIN, scaleX),
scaleY < ZERO_CUTOFF ? 0 : Math.max(SCALE_MIN, scaleY),
scaleZ < ZERO_CUTOFF ? 0 : Math.max(SCALE_MIN, scaleZ),
scaleX,
scaleY,
scaleZ,
quatX,
quatY,
quatZ,
Expand Down