Post 087

The Default Look, Done by the Book

The tonemap is the last thing that touches your render: the curve that turns raw HDR light into the image you actually see. The default was quietly wrong, mid-grey too bright and bright colours drifting. It now runs the real ACES 2.0 transform, the same one film and VFX pipelines use, matched to the reference closely enough that the difference is invisible. Your default 3D look is now the industry's default 3D look, out of the box.

Every 3D scene you render ends with one small, load-bearing decision: how to squeeze an enormous range of light, a sun a hundred thousand times brighter than a shadow, down into the handful of values a screen can show. That squeeze is the tonemap. It is the last thing that touches the image, and it decides, more than almost anything upstream, whether a render reads as “a photo of a thing” or “a computer drawing of a thing.” Lux’s default for 3D is ACES 2.0, the 2024 film-industry standard. It was quietly wrong, and this is the post about making it right.

What wrong looked like

Wrong here is not dramatic. Nothing was on fire. Mid-grey, the anchor the whole look hangs off, sat at byte 111 when the real transform puts it at 89. A bright grey at eight times white came out at 178 when it should roll up to 242. The numbers are small. The effect is not: every PBR material in every scene sat a little too bright through the midtones and rolled its highlights off a little too early, which is the exact recipe for “looks like a render.” You would not have been able to point at it. You would have felt it, fiddled with your lights, and blamed yourself.

The cause was plain enough. The old operator was an analytic fit, a tidy equation hand-tuned to look like ACES 2.0 without being it. ACES 2.0 is not a tidy equation. It is a chromaticity-linear tonescale with a per-channel reach and a gamut compressor, dozens of pages of reference maths, and a fit was never going to land it.

The scenic route

The obvious fix was to bake the real transform into a 3D lookup table, the standard trick for “make the GPU do a complicated colour thing fast.” A LUT needs an input range, the span of brightness it covers, and I picked one. Then I measured it, and it was wrong at the bright end, so I picked a wider one. That was wrong in the midtones, so I picked another. The number went five, then seven, then nine, then ten, and every time I looked at it harder it had moved again, like a witness changing their story. Each value was perfectly defensible right up until the next test undid it.

This is the part where the lesson was sitting in plain sight the whole time. ACES 2.0 does not come from a research paper I have to approximate. It ships inside OpenColorIO, the colour-management library that runs in Nuke and Blender and most of the film pipeline on Earth. And OCIO does not run ACES 2.0 as a lookup table. Ask it for its GPU code and it hands you the actual transform: the real tonescale, the real gamut compressor, a couple of tiny tables, the whole thing as a shader. The exact answer was one function call away while I was busy hand-carving a worse one.

Porting the real thing

So I stopped approximating and ported what OCIO itself runs. The ACES 2.0 transform now lives in Lux as the same algorithm the film world uses, translated line for line into the shader language the GPU speaks, fed by the two small tables OCIO ships with it. Checked against OCIO’s own output across a couple of thousand colours, the match holds to within a single step of 8-bit colour, measured the way the eye actually reads difference rather than the naive arithmetic that wildly overstates how far apart two blues are. Mid-grey lands on 89. Bright saturated colours, a blue LED, a hot emissive, a sun through atmosphere, roll off the way the reference does instead of skewing on the way out.

It is also smaller. The thing I was hand-tuning would have shipped as a 1.3 MB table. The real algorithm plus its two little tables is 17 KB. Doing it properly cost the binary less, not more, which is the rare kind of correct that also pays you back.

AgX and Tony McMapface, the other two film looks in the menu, stay as real lookup tables, because those genuinely are published as tables. ACES 2.0 is published as an algorithm. The fix was just to stop pretending otherwise and use each thing in the shape it actually comes in. This sits on top of the HDR pipeline that produces the light in the first place and the one shading path that feeds it, so the correction lands everywhere at once: every scene, every material, the same correct default.

What it buys you

The default 3D look in Lux is now the real ACES 2.0, matched to the reference closely enough that there is no difference to see. That matters for a dull, wonderful reason: predictability. A neutral grey sits where a neutral grey should. A bright red stays a believable red instead of going slightly radioactive at the top end. And the image you build in Lux reads the same as the same scene graded in any other tool that speaks ACES 2.0, because under the hood it is, to the byte, the same transform. You get to stop fighting the last step and spend that attention on the picture.

The whole detour comes down to one rule I have now written on the wall: when the job is to match something an authoritative tool already produces, go and get its answer before you invent your own. The library had it the entire time. I just had to ask.

← All posts