Raymarched 3D shapes
Draw whole 3D scenes from distance functions inside one fragment shader, with no meshes at all.
Cards
One live example to tweak, learn and copy.
How it works
Most 3D graphics start from triangles. RaymarchingDrawing a scene by sending a ray through each pixel and stepping along it by the distance to the nearest surface, read from a signed distance function, until it hits. Raymarching Drawing a scene by sending a ray through each pixel and stepping along it by the distance to the nearest surface, read from a signed distance function, until it hits. Signed distance field (SDF) A function that returns how far a point is from the edge of a shape, negative inside and positive outside. Zero is the outline itself. Smooth minimum A version of min that rounds the corner where two values cross. Applied to two distance fields it melts the shapes into one, with the blend width set by k.circle(p) = |p| − rlength(p) - r; a box, a torus or a capsule each take a line or two; and the
operators from 2D composition work unchanged, so min joins shapes and a smooth minimumA version of min that rounds the corner where two values cross. Applied to two distance fields it melts the shapes into one, with the blend width set by k.h = clamp(0.5 + 0.5 (b − a) / k, 0, 1); smin = mix(b, a, h) − k h (1 − h)
To see the scene, a fragment shader over a full-screen quad shoots one ray per pixel from a virtual camera. It steps
along the ray by the scene’s distance, which is always a safe step because nothing is closer, until the distance is
almost zero: the ray has hit. The surface normalA unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions. Normal A unit vector perpendicular to a surface, pointing the way it faces. Lighting and Fresnel effects compare it with other directions. Dot product Multiplies two vectors component by component and adds the results. For unit vectors it is the cosine of the angle between them, from 1 (same direction) to -1 (opposite).dot(a, b) = |a| |b| cos θ
What you get is a kind of geometry triangles can’t easily give: shapes that blend smoothly, repeat to infinity with a
mod on the point, bend and twist, all exact at any zoom and changing every frame with no mesh to rebuild. What it
costs is a loop in every pixel, each step evaluating the whole scene. That makes it the tool for full-screen demos,
backgrounds, title cards and effects inside a box, and the wrong tool for a game’s main geometry.
In an engine it runs on a full-screen rectangle (Godot’s ColorRect, a quad in front of the camera elsewhere). Mixing it with the engine’s own meshes needs the hit’s depth written out, which each engine handles differently.
Further reading
- dot (opens in a new tab)Reference
- Dot product (opens in a new tab)Reference
- Basic Lighting (opens in a new tab)Article
- Normal (geometry) (opens in a new tab)Reference
- Normal Mapping (opens in a new tab)Article
- Distance functions (opens in a new tab)Article
- Normals for an SDF (opens in a new tab)Article
- Signed distance function (opens in a new tab)Reference
- Smooth minimum (opens in a new tab)Article
- Domain repetition (opens in a new tab)Article
- mod (opens in a new tab)Reference
- Distance functions (opens in a new tab)Article
- Normals for an SDF (opens in a new tab)Article
- Ray marching (opens in a new tab)Reference
- Sphere tracing: a geometric method for the antialiased ray tracing of implicit surfaces (opens in a new tab)Paper