The Filters That Stopped Eating Light
Drop any 2D filter onto a 3D render and the picture used to sink into the dark, one filter darker, then two. A blur set to zero, a soft glow at neutral, an occlusion pass turned all the way down: none of them should touch a single pixel, and all of them did. Now a neutral filter leaves the frame exactly as it found it.


Reach for a filter and you expect a trade you can see: more blur, more glow, more grade. What you do not expect is for the lights to go down the moment the filter lands, before you have touched a single knob. But that is what happened to every 2D filter sitting downstream of a 3D scene.
A filter that dimmed the room
Wire a RenderScene into a Blur, set the radius to zero, and the blur does nothing. That is the whole point of zero. Yet the frame came back four to five times darker. Set an SSAO pass to intensity zero, which is the mathematical way of saying “no occlusion, leave everything alone,” and it dimmed the scene just the same. Chain two of these do-nothing filters and the picture sank twice as far into the black.
Here is a scene through a single zero-radius blur, before and after:


The one on the left is not a moody grade. It is the same render, the same lights, the same materials, run through a filter that was told to do nothing. A background that should read as a soft navy came out nearly black. The boxes lost their glow and turned to dried blood.
The seam between two ways of counting light
A 3D render stores its colours the way a screen wants them, with the brightness curve already baked in. When a filter reads that texture, the hardware helpfully undoes the curve so the filter can do its arithmetic on plain, linear light. That part was correct. The mistake was on the way out: the filter wrote its result straight back to disk without putting the curve back on. Light came in, got unwrapped for the maths, and then got stored still unwrapped, as if the flattened values were the final picture. Read back through a screen that expects the curve, every mid-tone landed far too dark. A background byte of 40 came out as 5. Do it twice and 5 becomes 1.
The filters were not darkening anything on purpose. They were dropping a step, and the picture paid for it every time.
Put the curve back
The fix is a single rule: a filter’s output carries the same wrapping as its input. Read a display-referred 3D frame, write a display-referred result. Read a linear HDR buffer, keep it linear. The maths in the middle stays exactly as it was, in plain linear light where it belongs. Only the final store changed, so the round trip closes and a neutral filter is once again a neutral filter.
Generators like Noise and Solid Color are untouched: they paint their own colours directly and never had an input curve to preserve, so they keep doing what they always did.
A test that cannot be fooled by the dark
The proof is the kind that is hard to argue with. Take the scene wired straight to the screen, no filter, and call that the truth. Then wire the same scene through a zero blur, a zero-intensity occlusion pass, and two zero blurs back to back. Every one of them now comes out identical to the truth, pixel for pixel, byte for byte. A frame that had quietly lost most of its light cannot match a bright one by accident. The gap between 5 and 40 is not a rounding error you can talk your way past.
This is the first repair in the 2D colour chain, and a deliberately narrow one: stop the destruction. Filters no longer eat light they were never asked to touch. The deeper work of running the whole 2D chain in full linear precision is a separate job for another day. For now the room stays lit.