Implicit Modeling
This page explains the math behind Chisel: how a shape is described by a signed distance field (SDF), how booleans, blends, rounding and modifiers work on those fields, and how the viewport draws them by ray marching.
Distance functions
A mesh describes a surface with a list of vertices and faces. A signed distance field describes it with a function instead: for any point p in space it returns the distance from p to the nearest point of the surface. The value is positive outside the shape, negative inside and zero on the surface.
For a sphere of radius 1 at the origin, the function is:
d(p) = |p| − 1
At (2, 0, 0) it returns 1: the point is one unit outside. At (0.5, 0, 0) it returns −0.5: half a unit inside. At (1, 0, 0) it returns 0: the point is on the surface. Nothing is stored except the centre and the radius, so the sphere has no facets to show when you zoom in.
A box works the same way: measure how far the point is outside each pair of faces and combine the three axes. Every Chisel shape has a function of this kind, and changing a setting such as a radius changes the function rather than rebuilding geometry.
Normals
The distance is defined everywhere in space, not only on the surface, and it changes smoothly from point to point. The direction in which it grows fastest, its gradient, is the surface normal:
n = ∇d(p)
Chisel shades each pixel with this normal, so the shading follows the surface exactly and there are no vertex normals to smooth.
Booleans
Two shapes A and B combine by comparing their distances at each point:
union(A, B) = min(A, B)
intersection(A, B) = max(A, B)
difference(A, B) = max(A, −B)
In the difference, −B swaps the inside and outside of B, so max keeps the points that are inside A and outside B.
An example with the sphere above and a second sphere of radius 1 centred at (1.5, 0, 0), at the point (0.75, 0, 0) halfway between them: both spheres return −0.25. The union is min(−0.25, −0.25) = −0.25, inside. The intersection is max(−0.25, −0.25) = −0.25, also inside. The difference is max(−0.25, +0.25) = +0.25, outside: the second sphere has removed that point from the first.
The result of each operation is again a distance field, so it can be combined further. Because no faces are cut, a Chisel Boolean cannot leave holes, overlapping faces or flipped normals.
Blends
When a boolean’s Blend is on, min is replaced by a smooth minimum. A common 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)
Where the two surfaces are more than k apart, smin gives the same value as min and the shapes are unchanged. Closer than k, the last term lowers the distance, which pushes the surface outward where the two shapes meet and forms the fillet. The radius is a distance in the scene, so the fillet has the same size at any zoom.
The five profiles (Round, Sharp, Soft, Tight, Chamfer) use different formulas for this transition. Chamfer, for example, joins the two surfaces with a straight slope instead of a curve.
Rounding
A single shape is rounded in a similar way: shrink it by r on every side, then subtract r from its distance.
rounded_box(p) = box(p, size − r) − r
The outer size stays the same, the edges become quarter cylinders of radius r and the corners quarter spheres. This is what the Rounding value of a shape does. The Smooth modifier applies one radius to every join and edge of a model.
Modifiers
Booleans combine fields. Most other modifiers change the point before the shape’s function is evaluated.
A mirror across X evaluates the shape at the point with its x made positive:
mirrored(p) = d( (|p.x|, p.y, p.z) )
A point on either side of the mirror plane gets the same answer, so the shape appears on both sides without a second copy. An Array repeats the point at an offset in the same way, and a Twist rotates it by an angle that grows along the axis.
Revolve turns the point’s distance from the axis into one coordinate and its height into the other, so a flat cross-section becomes a ring:
revolved(p) = profile( √(p.x² + p.y²) − offset, p.z )
Lattice moves the point through a cage, and Sweep measures the point against the nearest place on its path. In each case the result is still one distance field, so it can be joined, cut and blended like any shape.
Ray marching
To draw a pixel, the renderer needs to know where the line of sight from the camera through that pixel meets a surface.
It starts at the camera and evaluates the field. Suppose it returns 2.3: no surface is within 2.3 units of that point in any direction, so the renderer can move 2.3 units along the line without passing through anything. It evaluates the field again at the new point and moves again. The steps are long in empty space and get shorter near 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
The three limits in this loop are the first three settings of Render ▸ Quality:
- Max Steps: how many steps a ray may take before it stops. Rays that pass close to a surface at a shallow angle need many short steps, so silhouettes and surfaces seen edge-on break up first when this is too low.
- Hit Threshold: how close to the surface counts as a hit. Smaller values are more precise on thin parts and tight corners.
- Max Distance: how far a ray travels before it counts as a miss.
The smooth minimum never returns a larger distance than min, so a ray cannot step through a fillet.
Limits of the approach
- A distance field has no vertices to UV-unwrap or texture-paint. Convert to Mesh makes a mesh when you need one, and Convert to SDF turns a mesh into a Chisel shape.
- Every pixel is computed on the graphics card, and heavy scenes can slow the viewport. Quality and Performance have the settings for that.
See also
- Core Concepts: the terms this manual uses
- Rounding & Blending: the profiles and where blends are set
- Modifiers