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.

One circle, no geometry. Each shard is the same field evaluated in its own moving frame and clipped by its own cell.

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

  1. Undo the piece's motion and evaluate the original shape. Never carve a moving field.
  2. Test the rest point against the piece's own cell. Looking the cell up on screen freezes the partition.
  3. Cut with the bisector distance, not F2 - F1, or the seams change width along every edge.
  4. Widen the seam over time, or it reads as sliding rather than breaking.
  5. Give the cut interior its own brighter material. A fresh face should not look like the outside surface.
  6. Derive per-piece motion from a hash of the cell id, so it is stable frame to frame and needs no state.

All 61 notes How to use them Credits

If you find anything wrong, let me know at hello@andrewdetwiler.com and I'll get it updated.