A single fullscreen quad. The fragment shader marches rays through signed distance fields.
All geometry is mathematical — no vertices, no triangles, no scene graph.
One shader, one draw call. The GPU evaluates sceneSDF(p) at every pixel,
stepping along the ray until the distance is close enough to call it a surface.
For each pixel, a ray is cast from the camera through the view plane. The march loop
advances in steps: at each position p, it evaluates sceneSDF(p)
to get the distance d to the nearest surface, then steps forward by d.
If d < ε, the surface is hit. The normal is computed, lighting is applied,
and the color is tonemapped to the final pixel value.
The march loop is the frame budget bottleneck. Every pixel runs up to 96 steps,
and each step evaluates sceneSDF(p) — the entire scene distance function.
Fewer steps = missed geometry. More steps = slower frames. 96 is the current balance point.
All primitives are Lipschitz-1: gradient magnitude ≤ 1 everywhere.
This guarantees t += d never overshoots the surface.
length(p) - r.a to b, inflated by radius r. Used for tree trunks, limbs, bones.b. Sharp edges, useful for structures and AABB guards.r. Not exact but close enough for organic forms — canopies, clouds, torsos.k. min(a,b) - h²·k/4. Preserves Lipschitz-1.smin. Carves one surface from another with a smooth blend.| Metric | SDF Raymarcher | Polygon Mesh | Ratio |
|---|---|---|---|
| Draw calls | 1 | 456+ | 456x fewer |
| Frame time | ~8ms | ~12ms | 1.5x faster |
| March steps/pixel | 96 | N/A (rasterize) | — |
| Scene graph | None | Object3D tree | Eliminated |
| JS modules | 1 SDF module | ~26 modules | 26x fewer |
| Memory | ~40MB | ~120MB | 3x less |
| GC pauses | 2-5ms / 500ms | 2-5ms / 500ms | Same (both JS) |
At 60fps the budget is 16.7ms per frame. The march loop consumes the majority of it.
Each pixel evaluates sceneSDF up to 96 times. On a 1920×1080 display
that is ~199 million SDF evaluations per frame. The bottleneck is always the march loop.
Terrain uses p.y - terrainH(p.xz). This is not Lipschitz-1 — the FBM noise
terrain has gradient magnitude > 1, meaning the marcher could overshoot and miss thin features.
Solution: a 0.879 relaxation factor — multiply the step distance by 0.879
to ensure safe convergence. Costs ~14% more steps but prevents artifacts.
p.y > maxH.
If the current march position is above the maximum height any tree could reach,
skip the entire tree SDF — it cannot contribute to the distance.
Eliminates ~40% of tree SDF calls on sky-facing rays.
e = vec2(1,-1) * 0.0005 with 4 permutations.
Saves 2 SDF calls per normal computation (33% fewer).
sceneSDF_id(vec3 p) returns both the distance and a material ID.
After the march loop finds a hit, one extra call to sceneSDF_id
identifies which object was hit — terrain, tree, rock, structure —
so the shader can apply the correct material without branching during the march.
t += d is safe.
For height-field terrain (L > 1), step by t += d * 0.879.
The relaxation factor is derived from the maximum terrain gradient magnitude,
preventing surface penetration while minimizing wasted steps.
mod) helps for grids, but irregular placement means N evaluations per step.sceneSDF call, which means fewer steps fit in the frame budget, which means closer objects get missed. There is no free lunch — complexity trades against quality.
sceneSDF(vec3 p) is the Lithos kernel seam.
The function is the named entry point for the future Lithos native blob call. When Lithos
compiles sceneSDF to AGX bytecode, the GLSL implementation becomes the reference
oracle — the native dispatch must produce byte-identical output. Every SDF primitive
(sdSphere, sdCapsule, sdBox, sdEllipsoid,
smin, smax) maps directly to a Lithos font-table entry.
| Route | Mode | Notes |
|---|---|---|
/sign/sdf |
SDF Raymarcher | Explicit SDF mode for any home |
/sign |
SDF (default for Virgo) | Virgo defaults to SDF when no mode is specified |
Essence over geometry. Sensation over shape.
When drawing any object in SDF, pursue the essence of the thing, not its geometry. Encode sensation — texture, weight, wetness, warmth, roughness — into light and material response. Shape is necessary but insufficient; life comes from how light interacts with surface.
Never impose specifics into general methodology. Prescribing camera angles, petal counts, or segment numbers produces rigid recipes that work once and break on the next subject. The approach must be discovered from the subject itself.
If iterating, preserve what already has essence. Destroy what does not. Each pass must visibly change the result — safe incremental tweaks are failure.