The song brings a body, not only an outline

A silhouette is the same picture from every angle, so a scene that turns one is
showing you the same shape rotated. That is the ceiling the cast has been under:
it can be notched and hollowed and it still cannot be walked around.

So the identity generates an ASSEMBLY — two to six parts, each a primitive with
an offset, a scale, a rotation and a boolean op, all under a symmetry. The
symmetry is the load-bearing half: parts unioned at random positions read as
debris, the same parts folded read as designed, and only a designed object is
worth calling a protagonist. Prism parts are the existing 2D profile extruded,
so the solid and the silhouette stay one character rather than two generators
running side by side.

It travels as data like every other artifact: three vec4 rows per part in
u_formPart, plus the scalars. Scenes declare `consumes: ['form']` and get
castSDF3, castMarch, castSolid, castChorusSolid and castLit; with no identity
they fall back to the flat profile extruded, so the helpers are safe to call
unconditionally. The chorus is the same rows with fewer parts and its own
proportions — a relative, not a second generator, and no extra uniforms.

Three scenes carry it. Effigy is new and holds the object still while it turns.
Floating Geometry and Swarm were already loops of stamps and are now loops of
bodies; Swarm is what the chorus solid exists for. That is 34.8% of videos
containing a 3D cast, against 11.9% when only Effigy had it.

Measured, the outline does change rather than merely spin: over one turn the lit
area of Effigy's subject varies 14-113% against Soloist's 3-51% for the same
rotation. Whether that reaches the variety blocks is not yet measured, and
HOWTO-variety says so rather than claiming the win.

Four costs, each found by measuring rather than by reading:

  * every pixel evaluated every instance's field — Swarm at 59ms/frame against a
    60ms ceiling. Bounding-sphere reject first, now 12.3ms.
  * instances overlap several deep at the top of the size range, and marching
    all of them made Floating Geometry's own gate run for minutes. First-wins
    instead of last-wins, which was arbitrary either way.
  * a normal inside the march loop multiplies four copies of the SDF by the step
    count, because GLSL unrolls a fixed bound. Hoisted out.
  * the helpers in the shared preamble made all 68 scenes compile what 3 of them
    call. FORM_PREAMBLE is appended per scene instead.

The lint's backtick check was green through two of my own breakages: quoting a
name in a doc comment adds backticks in PAIRS, so parity survives and the
pair-scanner just re-partitions the file. It now finds where a shader literal
opens and requires the next backtick to be a real terminator — which
immediately found a second stray pair.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dejvino
2026-08-19 17:15:49 +02:00
co-authored by Claude Opus 5
parent 518e9ea731
commit 144c8c918e
12 changed files with 910 additions and 39 deletions
+62
View File
@@ -38,6 +38,7 @@ will be misfiled.
| **figure** | one or a few forms, its own composition | `cast, ink` |
| **field** | a continuous surface — noise, flow, terrain | `ink` |
| **treatment** | an effect over an image rather than an image | `ink` |
| **solid** | a camera in a space, marching a distance field | `form, ink` |
Accents are excluded. They are mostly-empty depth passes, not subjects.
@@ -113,6 +114,67 @@ you do.
---
## Recipe: `solid` — the cast in three dimensions
For a scene that has a camera in a space rather than a plane: a raymarcher, a
corridor, anything where the subject can be walked around. Declare
`consumes: ['form', ...]`.
```glsl
vec3 ro = vec3(0.0, lift, -dist); // eye, in object radii
vec3 rd = normalize(fw * lens + rt * p.x + up * p.y);
vec3 n;
float hit = castMarch(ro, rd, dist + 3.0, n);
if (hit > 0.0) col = castLit(n, rd); // lit in the track's palette
```
`castSDF3(vec3)` is the distance field if you want to place, repeat or carve
with it yourself; `castNormal3` is its normal. All of them fall back to the flat
profile extruded when a track brought no assembly, so they are safe to call
unconditionally.
### Many instances
For a scene that was stamping `castMain` in a loop — a field, a swarm, a belt —
swap the stamp for `castSolid`, which marches one instance orthographically in
its own frame:
```glsl
vec2 local = (p - centre) / size; // exactly what castMain was given
if (dot(local, local) > 1.6 || painted > 0.5) continue;
vec3 n;
float hit = castSolid(local, castTurn(yaw, pitch), n);
if (hit > 0.0) { painted = 1.0; col = castLit(n, vec3(0.0, 0.0, 1.0)); }
```
`castChorusSolid` is the same for a chorus member — the protagonist's body plan
with fewer parts and its own proportions, which is what a field of many should
be drawing.
**Both guards in that snippet are load-bearing**, and each was found by a
measurement rather than by review:
* the bounding-sphere reject, because without it every pixel evaluates every
instance's distance field — Swarm measured 59ms/frame at 4K against a 60ms
ceiling;
* `painted`, because instances overlap several deep at the top of the size
range, and marching all of them made Floating Geometry's own gate run for
minutes. Which instance wins where they overlap was always arbitrary, so
first-wins costs nothing.
A third rule lives in the contract rather than in your scene: take the surface
normal AFTER the march loop, never inside it. GLSL unrolls a fixed-bound loop,
so a normal in the loop body multiplies four more copies of the assembly SDF by
the step count. For the same reason these helpers are compiled only into scenes
that declare `form` — see FORM_PREAMBLE.
Worth the cost only if the shot MOVES relative to the object. A solid held at
one angle is a silhouette with shading, and `cast` draws that for a fraction of
the price — the assembly earns its keep through the outline changing, which
needs either the object turning or the camera travelling.
---
## Recipe: `field` and `treatment`
There are no elements to replace, so this is one edit plus a judgement.