Post 122

A Cleaner Cut

Cone culling from Mind the Gaps was mathematically correct but ran on clusters shaped by whatever order triangles happened to load in. Lux now clusters meshes the way the industry actually does it, and the optimisation finally has something worth optimising.

Mind the Gaps fixed the maths behind cone culling: the trick where Lux tests a batch of up to 124 triangles at once and skips the whole batch if none of them can possibly face the camera. Three separate bugs in that maths got fixed. What the post didn’t dwell on is where the batches themselves came from, because at the time that wasn’t the problem.

It became the problem. The batches were built by an in-house clusterer that grouped triangles in roughly the order they loaded, nudged toward whichever neighbour was cheapest to add next. On a flat surface that produces sensible batches almost by accident, all the triangles already agree on a facing direction. On a curved one, the same greedy process happily welds triangles from opposite sides of the curve into a single batch, and a batch with several facing directions has no facing direction worth testing. The now-correct maths had, on exactly the geometry it existed for, very little to actually cull.

Borrowing the industry’s clustering, not just its formula

The fix is to stop building batches in-house at all. Lux now hands every mesh to meshoptimizer, the library most of the industry already reaches for to do this, and takes its bounds too rather than deriving them by hand a second time. Lux’s batch-size cap was already set to match the hardware, 128 vertices and 124 triangles is the same neighbourhood NVIDIA’s own mesh-shader guidance and Unreal’s Nanite both settle on. What was missing was a clusterer that could actually fill it with triangles that belong together.

On SheenChair’s fabric, the difference is stark: the old clusterer produced 646 small, often poorly-shaped batches out of 26,176 triangles; the new one produces 227 fuller ones, each with a batch of triangles that mostly agree on which way they’re facing. Fewer batches, less per-batch bookkeeping, and cone culling skipping a properly comparable share of them, this time because the batches deserve it.

Nothing to screenshot, again

Same closing note as last time: a correct cull changes no pixels, only how much gets drawn to produce them. The chair, the fabric, the helmet all render bit-identical to before. A CPU twin of the cull, run against the real fabric mesh, checks that nothing gets skipped that shouldn’t be, and a second check confirms culling is still doing real work rather than quietly opting out. What changed is invisible on screen and, with any luck, increasingly present in your frame time.

← All posts