Shattering with a Voronoi field
Built on cellular noise, SIGGRAPH 1996 by Steven Worley. Sources for this reference
Built on Voronoi edges by Inigo Quilez. Sources for this reference
Breaking a shape usually means generating geometry: cell polygons, a mesh per shard, a physics body each. None of that is required if the shape is already a field. A Voronoi partition plus one trick gives you the whole effect per pixel.
The trick is evaluating in rest space
The instinct is to move the pieces and then try to cut them out of the field where they now are. That does not work, because the shape is defined where it started, not where the piece has flown to.
So invert it. The pixel you are shading cannot just look up which cell it is in, because the cells have moved. Looking the cell up at the screen position is the same mistake in disguise: the partition stays frozen on screen and only the shape slides around underneath it. Instead ask each nearby piece in turn. Undo that piece's motion and rotation, and keep the answer only if the point you land on is inside that piece's own cell. Every shard is the same function sampled in a different frame, and the fragments fit together perfectly because they were never actually separated.
vec2 local = p - drift; // undo translation
local = rotate(local - c, -spin) + c; // undo rotation about the piece
float body = sdCircle(local, 0.62); // the ORIGINAL shape, in rest space
The cell boundary is the cut
The common Voronoi edge metric is F2 - F1, the difference between the
distances to the two nearest sites. It is zero on a cell boundary, but it is not a
distance. Inigo Quilez shows why: it swells and shrinks with the spacing of the two
sites, reading up to twice the true distance in the middle of an edge and less toward
the corners, so a seam cut from it changes width along every edge. The exact border
distance is not much harder. Each neighboring site cuts the plane along the bisector
between it and yours, the cell is what survives the cuts, and the signed distance to
the cell is the largest of the signed distances to those bisectors. Intersect the
body with it and each piece keeps only the part of the shape that belongs to it.
Widening that seam over time is what makes it read as breaking rather than sliding. A shard that separates without a visible cut looks like the shape simply came apart at a texture, not along a break.
What this costs
Every pixel undoes the motion of every piece that could cover the shape (64 here), which is a hash and a rotation each. Almost all of them are rejected by two cheap tests (the rest point is outside the circle, or too far from that piece's site) before any Voronoi work, and the one or two survivors pay for the 24 bisectors of their own cell. No buffers, no mesh generation, no per-shard draw calls. The price of more shards is more candidates per pixel, so going from 40 shards to 400 is still a single number, but it is no longer a free one.
The limit is honest though: every pixel pays for the partition whether or not it is near a break, and pieces cannot collide with anything, because there are no bodies to collide. This buys a visual break, not a simulation.
Rules of thumb
- Undo the piece's motion and evaluate the original shape. Never carve a moving field.
- Test the rest point against the piece's own cell. Looking the cell up on screen freezes the partition.
- Cut with the bisector distance, not
F2 - F1, or the seams change width along every edge. - Widen the seam over time, or it reads as sliding rather than breaking.
- Give the cut interior its own brighter material. A fresh face should not look like the outside surface.
- Derive per-piece motion from a hash of the cell id, so it is stable frame to frame and needs no state.
If you find anything wrong, let me know at hello@andrewdetwiler.com and I'll get it updated.
One email when something new goes up. No newsletter, no schedule, nothing else.
Double opt-in, so watch for a confirmation email. Unsubscribe any time.