Splats · interface
SplatData
A capture packed for the GPU, plus the two things the CPU still needs.
Explained in Gaussian splats.
interface SplatDataimport type { SplatData } from '@driftengine/splats';In depth
positions survives packing because the sorter reads it every time the view turns, and it is
the only per-splat attribute that does. Everything else is in packed and is never looked at
again by JavaScript.
This takes ownership of the arrays handed to packSplats. They are twelve and four bytes a
splat, so copying a million-splat capture would be sixteen megabytes of duplicate to no purpose;
the cost is that a caller must not go on mutating a source after packing it, which is stated
here because nothing enforces it.
Properties
| Name | Type | Description | |||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
countreadonly | number | ||||||||||||||||||||||||||
positionsreadonly | Float32Array | Three per splat. See above: the sorter's input, and the reason this is not packed away. | |||||||||||||||||||||||||
packedreadonly | Uint32Array | wordsPerSplat per splat — texel 0 is position and colour, texel 1 is the covariance, and
texel 2 is the l=1 band where the capture carries one. | |||||||||||||||||||||||||
wordsPerSplatreadonly | number | SPLAT_WORDS or SPLAT_WORDS_SH1. Everything that indexes packed reads this rather than a
constant, and FORMAT.md §4.7 anticipated exactly this: a SPLT block already carries its
own wordsPerSplat and says "a capture that carries spherical-harmonic coefficients later
fits by raising it and nothing else". | |||||||||||||||||||||||||
boundsMinreadonly | Float32Array | The capture's axis-aligned extent, for the frustum test that precedes a sort. | |||||||||||||||||||||||||
boundsMaxreadonly | Float32Array | ||||||||||||||||||||||||||
extentsreadonly | Float32Array | One per splat: the largest of its three standard deviations, in the capture's own units.MoreWhat the budget ranks by, and the second attribute that survives packing. A splat's contribution to the picture is roughly how many pixels it covers, which is its world extent over its distance to the camera; the distance is per-frame and this is the half that is not. The largest sigma bounds the projected radius from above for every orientation, so it ranks correctly without a per-splat projection. What it costs is four bytes a splat — four megabytes at a million, on top of the twelve
| |||||||||||||||||||||||||
sphericalHarmonicsreadonly | number | How many f_rest_* coefficients the source carried, of which this kept the first band.MoreZero for a format that has none. Nine are read as of 2026-08-27 and the rest are not, and the costing that decided which is below, kept because it is the argument against raising it. What view-dependent colour costs, costed 2026-08-27 and built the same dayA splat with no harmonics is A degree-
Against the mobile budget of Degree 1 is the band that earns its texel. The l=1 lobe is broad and directional — it is what makes glass, a wet surface and a polished floor read as themselves — where degrees 2 and 3 are the sharp specular detail that costs four and six times as much. Nine coefficients fit one texel as bytes with a per-splat scale beside them, rather than halves: nine halves is 18 bytes and would not fit, and a per-splat scale spends the sixteenth byte on the accuracy that quantising to a capture-wide range would have thrown away. What is still unmeasured, and it is the number that would refine this: how much of the 6x
amplification a real texture cache absorbs, since six consecutive invocations read the same
texels. Answering it needs a GPU timing of the splat pass against a capture, and this checkout
has none — | |||||||||||||||||||||||||
shDegreereadonly | 0 | 1 | How many bands of that expansion this capture actually carries: 0 or 1.MoreSeparate from |