Case study / Rendering architecture
SDF Raymarcher URP
Signed-distance-field rendering inside a Unity URP scene, kept honest about per-pixel cost. The interesting part is not that SDFs can look smooth. The internet has noticed. The interesting part is making the render path behave predictably inside URP.
The problem
SDFs make smooth procedural geometry, soft blending, constructive solid operations, and compact shape definitions inexpensive to express. They are less inexpensive to render. They live in fragment shaders. Every pixel runs a loop. The GPU is asked to do math the engineer did not write a mesh for.
The goal here was not a shiny demo. It was a working render path that composes with the rest of a URP scene, respects the depth buffer, and survives a profiler without asking for emotional support.
The constraint
Three constraints shaped the architecture. First: the SDF content should enter URP through one extra ScriptableRenderFeature, not a small festival of render passes. Multiple passes multiply bandwidth cost, and bandwidth is where good ideas go to be humbled.
Second: the implementation should stay readable on mobile-class budgets. The target is not 60 FPS on a machine that could heat a studio apartment. It is a path that can be reasoned about when the frame already has other obligations.
Third: raymarched objects need to compose with regular geometry. They must intersect, occlude, and be occluded by polygonal content. Drawing over the top of everything is not integration. It is vandalism with a shader.
The tradeoffs
A lower internal resolution for the raymarched pass is often the first serious win. Most viewers notice edges and composition before they notice interior shading. Keeping polygonal foreground content at full resolution while raymarching at a lower resolution is not cheating. It is an admission that pixels have rent.
The loop needs a fixed iteration cap with early exit on convergence. Unbounded iteration is a profiler hazard with a poetic name. The cap should be tuned against scene complexity, not left at a heroic default that burns cycles on geometry that has already converged.
Normals can be approximated with tetrahedral sampling instead of central differences. Four samples instead of six, slightly lower quality, faster in practice, and usually indistinguishable once the scene moves like a living thing instead of a shader seminar.
What it bought
The render path buys geometry that polygonal meshes do not represent elegantly: smooth booleans, organic blends, infinite tiling, procedural shapes, and content that can be described by functions instead of exported from a modeling tool.
More importantly, it does this without forking URP. A fork is sometimes necessary. It is also a pet that eats weekends. A small ScriptableRenderFeature is easier to audit, move, and delete if the experiment stops earning its place.
What it cost
Authoring is harder. Adding a new SDF primitive is a shader code change, not a drag-and-drop operation. That is acceptable for a small custom scene and bad for designer-driven content. The tool knows what it is. This is already an improvement over many tools.
Object count has a hard ceiling. Each additional primitive adds shader instructions to every pixel of the SDF pass. The cost is linear and visible in the profiler before it becomes a frame-rate disaster. This is useful. Disasters that announce themselves early are the polite kind.
Evidence
The public repository is MIT-licensed and intentionally small. The render feature, shader entry point, and SDF library are meant to be traceable in one sitting. The code is the current evidence: compact integration, narrow scope, and no attempt to pretend a technical proof is a full authoring system.
The next evidence pass is measured capture: frame timings, pass ordering, and visual comparison images. Until those are published, the honest claim is architectural, not numerical. This is less exciting than invented numbers, but it has the advantage of not being invented.