Splats · function

resolveSplatView

Fill out with the camera, in the space the capture's own positions are written in.

Explained in Gaussian splats.

function resolveSplatView(view: ArrayLike<number>, model: ArrayLike<number>, out: SplatViewLocal): void
import { resolveSplatView } from '@driftengine/splats';

Parameters

ParameterTypeDescription
viewArrayLike<number>
modelArrayLike<number>
outSplatViewLocal

In depth

One sorter serves one batch, and this is what makes that affordable. A batch has a model matrix so that two captures can compose in one scene; its splat positions are in its own frame and the sort reads them there, so asking the sort for a world direction would mean transforming a million positions every time the view turned. Transforming the camera instead is six numbers.

The direction is the model's transpose and not its inverse, and the two disagree exactly where it matters. What the sort needs is an ordering that matches the depth those splats really have once the model has moved them, and the world depth of a capture-space point p is dot(M p + t - c, d), which rearranges to dot(p, Mᵀd) plus a constant. So Mᵀd is the direction that orders correctly for any invertible model, including one with a non-uniform scale; M⁻¹d is the direction that would be right if the transform were a rotation, and a plausible-looking answer everywhere else. splatView.test.ts asserts the ordering rather than the arithmetic, which is why the model it uses is stretched.

The origin is the inverse, because that genuinely is a point: M⁻¹(c − t) is where the camera sits in the capture's frame, and it is what the budget's distances are measured from. It also happens to be exactly the point whose projection along Mᵀd cancels the constant above, so the two halves agree by construction rather than by arrangement.

What this gives up: the ordering is along the view axis rather than by distance to the camera point, so two splats at equal depth and far apart across the frame are ordered by a plane rather than by a sphere. That is the ordering every splat renderer uses and it costs nothing until a capture wraps around the viewer. What would make it wrong is exactly that case — standing inside a capture at a wide field of view, where the error shows at the frame's corners.

A singular model falls back to the world rather than to NaN. This is reached from a frame and the loop may not throw; a NaN direction is a sort in which every comparison is false, which is silently input order rather than a visible fault.