Chisel

A signed distance field (SDF) describes a shape by one number at every point in space: how far that point is from the shape’s surface, negative inside and positive outside. Raymarching renders it by walking a ray from the camera in steps of that distance until it lands on the surface. Chisel’s shapes, booleans and fillets are all distance fields, and its viewport is a raymarcher. This page shows the math behind that.

A shape is a question

A polygon mesh answers the question “where is the surface?” with a list: these vertices, joined by these faces. Chisel’s shapes answer a different question:

How far am I from the surface?

Pick any point in space and the shape gives you back one number: the distance to its nearest surface. Outside the shape the number is positive. Inside it is negative. Exactly on the surface it is zero. That’s the whole idea: a signed distance field is a shape described by how far away it is from everywhere.

Take a sphere of radius 1 sitting at the origin. Its distance function is about as simple as math gets:

d(p) = |p| − 1

Stand at (2, 0, 0) and it tells you 1. You are one unit outside. Stand at (0.5, 0, 0) and it says −0.5, half a unit inside. Stand at (1, 0, 0) and it says 0: you’re on the skin. There is no list of points anywhere. The sphere is a rule, and the rule is true at every point in space, at any zoom, forever. That is why a Chisel sphere is round no matter how close you get.

A box is only slightly more work (measure how far you are outside each face, combine the three axes) and every other primitive is a similar few lines. Because the shapes are rules and not point lists, a Chisel scene is tiny, and editing a radius changes the rule rather than rebuilding geometry.

The field knows which way is out

The word field matters. The distance isn’t only defined on the surface; it’s defined at every point, and it changes smoothly as you move. Walk a tiny step in any direction and see how the distance changes, and you learn which way the surface faces. In math terms the gradient of the field is the surface normal:

n = ∇d(p)

Chisel shades every pixel from that gradient. There are no vertex normals to smooth, no faceting to hide, and the shading is exactly as smooth as the surface itself, because it is the surface.

Combining shapes is arithmetic

Two shapes A and B combine with plain arithmetic. The distance to their union (everything inside either) is the smaller of the two distances at that point:

union(A, B)        = min(A, B)
intersection(A, B) = max(A, B)
difference(A, B)   = max(A, −B)

Read the difference line slowly: negating B turns it inside out, so max keeps what’s inside A and outside B. That’s a cut.

Run the numbers on our sphere and a second sphere of radius 1 centred at (1.5, 0, 0). At the point (0.75, 0, 0), halfway between them: sphere A says −0.25, sphere B says −0.25. Union: min = −0.25, inside. Intersection: max = −0.25, also inside, the lens where they overlap. Difference: max(−0.25, +0.25) = +0.25, outside. B has scooped that spot out of A.

No faces were split and nothing became non-manifold: each boolean is one comparison per point, and the result is another distance field you can keep cutting into. This is why a Chisel Boolean cannot produce a broken mesh. There is no mesh.

Where the fillet comes from

Push Blend Radius on a boolean and the seam becomes a fillet. Nothing is added to make that happen; min is simply replaced by a soft minimum. The classic one, for a blend radius k, is:

h = clamp(0.5 + 0.5·(B − A) / k, 0, 1)
smin(A, B, k) = mix(B, A, h) − k·h·(1 − h)

Wherever the two surfaces are further apart than k, the plain minimum wins and nothing changes. Within k of both, the two fields negotiate, and the − k·h·(1 − h) term pushes the surface outward exactly where they meet. That bulge is the fillet. The radius you set is a real distance in the scene, not a subdivision count, so the fillet stays perfectly round at any zoom.

The five profiles (Round, Sharp, Soft, Tight, Chamfer) are five different ways of writing that negotiation. Chamfer, for instance, joins the two surfaces with a straight line instead of an arc.

The same trick rounds a single shape. Shrink a box by r on every side and then subtract r from its distance:

rounded_box(p) = box(p, size − r) − r

Every point that was within r of the shrunken box is now inside, so the corners become quarter-spheres and the edges quarter-cylinders. That is the Rounding slider on every primitive: an offset of the field, which is why it costs nothing. The Smooth modifier applies the same idea to the whole model at once: one radius, every join and edge sharper than it rounds up to it.

Modifiers bend space, not shapes

Booleans combine fields. The other modifiers change the question before it reaches the shape.

Take a mirror across X. Instead of building a second copy, Chisel takes your point p, flips the sign of x, and asks the original shape that question:

mirrored(p) = d( (|p.x|, p.y, p.z) )

A point on the left of the mirror plane is answered as if it were on the right, so the shape appears on both sides. One shape, two appearances, zero extra cost. Array is the same trick with a repeating offset. Twist rotates p by an angle that grows along the axis before asking. Revolve collapses the point onto a half-plane (its distance from the axis becomes one coordinate, its height the other) so a flat profile answers as a full ring:

revolved(p) = profile( √(p.x² + p.y²) − offset, p.z )

Lattice warps p through a cage; Sweep finds the nearest point on the path and asks the cross-section from there. In every case the shape is left alone and space is bent around it, which is why a bent shape is still exactly one shape, still watertight, still cuttable.

Raymarching: finding the surface

Every pixel needs to know where the surface is along its line of sight. A mesh renderer tests triangles; Chisel marches.

Fire a ray from the camera through the pixel. Ask the field: how far is the nearest surface? Say it answers 2.3. You don’t know which direction that surface lies in, but you know for certain there is nothing within 2.3 units in any direction, so you can safely walk 2.3 units along the ray. Ask again. Walk again. Each step is as long as it can safely be, so the ray covers empty space in a few big strides and slows down only as it approaches a surface.

t = 0
repeat:
    d = field(camera + t · direction)
    if d < hit_threshold:  hit: shade this pixel
    t = t + d
    if t > max_distance or too many steps:  miss: background

That loop is the whole renderer, and its three knobs are the three sliders at the top of Render → Quality:

  • Hit Threshold: how close counts as touching. Smaller is more exact, especially for thin parts and tight corners.
  • Max Steps: how many strides a ray may take before giving up. Rays that graze a surface at a shallow angle take many small steps, which is why silhouettes go chunky first when this is too low.
  • Max Distance: how far a ray travels before it’s called a miss.

The soft minimum plays nicely here: it only ever reports a distance smaller than the plain minimum would, so a ray marching through a blended join steps a little more cautiously and never jumps through the fillet.

Compared with meshes

Rasterization (EEVEE)Path tracing (Cycles)Raymarching (Chisel)
InputTrianglesTrianglesDistance functions
PrecisionMesh densityMesh densityMathematically exact
BooleansMesh ops, can failMesh ops, can failOne comparison per point
FilletsExtra geometryExtra geometryA term in the formula
SpeedVery fastSlowReal-time for SDF

Trade-offs

  • No UV painting until you convert to mesh
  • GPU-bound: needs a reasonable graphics card
  • Very dense scenes can slow the viewport; Quality and Performance are the controls for that
  • Meshes come in through Convert to SDF and go out through Convert to Mesh

See also