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.
How it composes with URP
The raymarched content enters URP through a single ScriptableRenderFeature inserted after opaque geometry. The pass reads the camera depth texture, marches against the scene's SDF library, writes both color and a representative depth value, and lets the rest of URP keep doing its job. There is no second camera, no manual blit chain, and no surprise reordering of passes that breaks transparents or post-processing later.
The pass cooperates with the depth buffer in both directions. Polygonal geometry can occlude the SDF surfaces, the SDF surfaces can occlude polygonal geometry, and the boundary between the two is one fragment shader comparison, not a custom depth-resolve pipeline. That single decision is most of what keeps the integration calm.
The shader hot path
Inside the loop, three details carry most of the cost. Iteration count is the obvious one: a high cap looks safer and burns through silicon on pixels that converged early or never had geometry to hit. A per-scene cap, plus early exit on a distance threshold relative to camera depth, lets the loop pay only for pixels that have something to march toward.
The second is the SDF library itself. Each primitive added is a branch and a distance evaluation inside every iteration, for every pixel of the pass. Smooth unions and intersections multiply that further. The discipline is to keep the library small and deliberate, not to expose every primitive a graph editor might want to add later.
The third is the normal calculation. Six-sample central differences are the textbook approach and the slowest. Four-sample tetrahedral normals look almost identical on lit surfaces moving through a scene, and the saved samples are the kind of cost that disappears from the profiler without anyone mourning it.
What this is not
This is not a Shadertoy demo, a node-graph authoring system, or a replacement for Unity's mesh pipeline. Shadertoy demos belong in browsers and serve a different purpose. Node graphs are wonderful for designer-driven content and would change the scope of this project from a render-feature experiment into a tool with its own authoring story.
What this is: a small, auditable render path that proves SDFs can live next to URP content without forking the pipeline and without producing a frame the profiler refuses to forgive. The scope is intentionally narrow because narrow scope is what makes the cost honest.
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.