Builtin Library
README.md
gaussianSplats
Draws every Gaussian splat cloud registered in
@builtin::systems.gaussianSplats.gaussianSplats.
On an entity's first frame the feature decodes its capture
(renderer.splat.components) and uploads the packed record pool — plus the
spherical-harmonics pool when the capture carries one — to named GPU buffers.
Every frame after that it sorts each cloud against the camera being drawn, then
enqueues one kind = "splat", channel = "gaussian" pass per cloud into a
feature-owned RGBA16F target. A full-screen pass at afterLighting lays that
target over the scene colour with a premultiplied "over".
An empty registry enqueues nothing.
Velocity
A cloud is placed rigidly by its entity's world transform, so every splat in it
undergoes the same screen-space displacement. The draw is handed where the
transform stood last frame alongside where it stands now, and writes each
splat's displacement into a second target — composited "over" under exactly the
alpha its colour was, so the target resolves to (coverage-weighted mean displacement, coverage).
Two more passes fold that into the frame's velocity buffer:
splat_velocity_merge weights the clouds' displacement against what the
geometry passes wrote by that coverage, and a copy lays the result back over
@scene.motion, because a pass cannot sample the attachment it renders into.
All three passes run at afterLighting, ahead of the passes that read the scene
colour and the velocity buffer together — a denoiser's history term (order 50),
motion blur (310) and the temporal antialiasing resolve (600) — so the cloud is
in the image and the cloud's motion is in the velocity buffer by the time any of
them looks. gaussianSplats.setVelocity(false) turns the chain off for the
whole feature — the clouds share one accumulation target, so what lands in it is
a property of the frame rather than of a single cloud — and with it off neither
velocity target is allocated and neither pass is enqueued.
The sort
splat_cull assigns each splat a log-spaced depth bucket and seeds the identity
permutation. A stable LSD radix sort then orders the cloud on that key, eight
bits per pass; each pass runs the shared gpuSort passes radix_hist →
radix_scan → radix_scatter, and they ping-pong the permutation between the
order buffer and a scratch buffer so the last one lands in order.
Alpha compositing is order dependent and the correct order changes with the camera, so it cannot be baked into the capture. A splat's slot comes from its rank among earlier same-digit splats in its own block, which makes the permutation a function of the capture and the camera alone — the same camera produces the same pixels.
A culled splat takes the key one past the last real bucket, so it lands in the
tail; the last pass resolves it to 0xffffffff, and that slot collapses its
quad. This is what lets the pass issue a fixed instance count with no read-back
and no indirect draw arguments.
Buffers per cloud
| Buffer | Contents |
|---|---|
gaussianSplats.rec.<id> | Packed splat records (vertex-pulled by the draw). |
gaussianSplats.sh.<id> | Quantized higher-order SH, when the capture has any. |
gaussianSplats.view.<id> | CullUniforms for the frame's camera. |
gaussianSplats.keys.<id> | Per-splat depth bucket. |
gaussianSplats.order.<id> | The sort's output — the draw order. |
gaussianSplats.scratch.<id> | The permutation the radix passes ping-pong through. |
gaussianSplats.hist.<id> | Per-block digit populations, then their prefix sum. |
gaussianSplats.stats.<id> | Surviving splat count. |
gaussianSplats.sort.<id>.<n> | Constants for radix pass n. |
They are freed when the entity leaves the registry, despawns, or points itself at a different capture.
Feature-owned targets
Screen-sized RGBA16F, one set for the whole feature rather than one per cloud.
| Target | Contents | Allocated |
|---|---|---|
gaussian_splats_accum | Every cloud's premultiplied colour, composited "over". | The first frame a cloud draws. |
gaussian_splats_velocity | Every drawn cloud's displacement and coverage. | The first frame a cloud draws while the clouds report their motion. |
gaussian_splats_velocity_merged | The frame's velocity buffer with the clouds folded in. | Beside the one above. |
They are released when the feature is destroyed.