Skip to content

3D SDFs

A 3D signed-distance function maps a point in space to a single f32: the shortest distance from that point to the surface of a solid. The sign tells you which side you are on — negative inside the volume, positive outside, zero exactly on the surface. With a sphere-tracer that steps along each ray by the current distance value, that one number is enough to render lit, intersected, smoothly-blended geometry without a mesh.

The registry separates primitives (sphere, box, torus, …) that return a distance, from operators (op_*) that combine or warp distances. Booleans (op_union, op_subtract, op_intersect, plus their smooth variants) take two distances and return one. Domain operators (op_bend, op_twist, op_mirror, op_repeat, op_elongate) transform the input point before you sample a primitive. op_round and op_onion reshape any distance into a rounded or hollow version.

Every entry below is a single pure WGSL function in the registry. Pull one (or several, for compositions) into a Shader::new call, then call them from a fragment stage that runs a small sphere-trace and shades the hit:

let main = r#"
fn scene(p: vec3<f32>) -> f32 { return sphere(p, 0.7); }
@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
// ... sphere-trace `scene` and Lambert-shade the hit ...
}
"#;
let shader = Shader::new(&["sdf/sphere", main])?;

Each preview below renders the SDF on a 256×256 fragment with a fixed camera at (0, 0, -2.5) looking down +z, sphere-traces the scene, and applies a single Lambert light. Tune the parameters to your own scene; the registry shape is just the function.

Axis-aligned box centered at the origin with half-extents b.

fn box(p: vec3<f32>, b: vec3<f32>) -> f32

sdf/box preview

Capsule (pill) between endpoints a and b with radius r.

fn capsule(p: vec3<f32>, a: vec3<f32>, b: vec3<f32>, r: f32) -> f32

sdf/capsule preview

Infinite cone with half-angle encoded as c = vec2(sin, cos). Apex at the origin, axis along +y. Combine with a plane or box to bound it.

fn cone(p: vec3<f32>, c: vec2<f32>) -> f32

sdf/cone preview

Capped vertical cylinder with half-height h and radius r.

fn cylinder(p: vec3<f32>, h: f32, r: f32) -> f32

sdf/cylinder preview

Approximate signed distance to an ellipsoid with axis radii r. The gradient is not unit length, but it is close enough for sphere-tracing.

fn ellipsoid(p: vec3<f32>, r: vec3<f32>) -> f32

sdf/ellipsoid preview

Hexagonal prism with h.x = circumradius and h.y = half-height.

fn hexagonal_prism(p: vec3<f32>, h: vec2<f32>) -> f32

sdf/hexagonal_prism preview

Regular octahedron with half-diagonal s. Exact SDF.

fn octahedron(p: vec3<f32>, s: f32) -> f32

sdf/octahedron preview

Domain operator: bends space around the Z axis with bend rate k. Returns the transformed point — pass it to a primitive. The preview applies it to a long rounded box.

fn op_bend(p: vec3<f32>, k: f32) -> vec3<f32>

sdf/op_bend preview

Domain operator: stretches an SDF along axes by h (per-axis distance to extend). Returns a displaced point. The preview elongates a sphere along x.

fn op_elongate(p: vec3<f32>, h: vec3<f32>) -> vec3<f32>

sdf/op_elongate preview

Boolean intersection of two distance values: max(a, b). The preview intersects a sphere and a box.

fn op_intersect(a: f32, b: f32) -> f32

sdf/op_intersect preview

Domain operator: reflects the negative half-space onto the positive half along the unit axis a. The preview mirrors an offset sphere.

fn op_mirror(p: vec3<f32>, a: vec3<f32>) -> vec3<f32>

sdf/op_mirror preview

Hollows any signed distance into a thin shell of thickness t: abs(d) - t. The preview applies it to a sphere and slices it open with a box subtraction so you can see the shell.

fn op_onion(d: f32, t: f32) -> f32

sdf/op_onion preview

Domain operator: tiles the input point on an infinite grid with cell size c. Returns a displaced point. The preview tiles a small sphere.

fn op_repeat(p: vec3<f32>, c: vec3<f32>) -> vec3<f32>

sdf/op_repeat preview

Uniformly inflates or rounds any SDF by r: d - r. The preview applies it to an octahedron.

fn op_round(d: f32, r: f32) -> f32

sdf/op_round preview

Smooth intersection of two distances with blend radius k. The preview intersects two offset spheres.

fn op_smooth_intersect(a: f32, b: f32, k: f32) -> f32

sdf/op_smooth_intersect preview

Smooth subtraction a − b with blend radius k. The preview subtracts a sphere from a box.

fn op_smooth_subtract(a: f32, b: f32, k: f32) -> f32

sdf/op_smooth_subtract preview

Quadratic-polynomial smooth union with blend radius k. The preview fuses two offset spheres.

fn op_smooth_union(a: f32, b: f32, k: f32) -> f32

sdf/op_smooth_union preview

Boolean subtraction a − b: max(a, -b). The preview cuts a sphere out of a box.

fn op_subtract(a: f32, b: f32) -> f32

sdf/op_subtract preview

Domain operator: twists space around the Y axis with twist rate k. Returns the transformed point. The preview applies it to a long box.

fn op_twist(p: vec3<f32>, k: f32) -> vec3<f32>

sdf/op_twist preview

Boolean union of two distance values: min(a, b). The preview unites a sphere and an offset box.

fn op_union(a: f32, b: f32) -> f32

sdf/op_union preview

Signed distance to a plane with normal n (normalized inside) and offset h: dot(p, normalize(n)) + h. The preview intersects a tilted plane with a bounding sphere so the trace terminates.

fn plane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32

sdf/plane preview

Box with half-extents b and rounded corners of radius r (subtracted from the box).

fn rounded_box(p: vec3<f32>, b: vec3<f32>, r: f32) -> f32

sdf/rounded_box preview

Signed distance to a sphere of radius r centered at the origin: length(p) - r.

fn sphere(p: vec3<f32>, r: f32) -> f32

sdf/sphere preview

Torus in the XZ plane with major radius t.x and tube (minor) radius t.y.

fn torus(p: vec3<f32>, t: vec2<f32>) -> f32

sdf/torus preview