Experimenting with alternative tonemappers while keeping Unreal's color grading workflow

I’ve been experimenting with alternative tonemappers in Unreal recently.

There are already several ways to replace the default tonemapper (existing plugins, Post Process Materials, SceneViewExtension, etc). But each approach I’ve looked at comes with tradeoffs.

What I’m specifically interested in is something narrower: changing the tone curve while keeping Unreal’s existing Post Process Volume color grading workflow intact.

One of the things that sent me down this rabbit hole was how bright saturated colors behave through the default Filmic tonemapper. As intensity increases, saturated colors can shift significantly. Red towards orange/yellow is a particularly obvious example.

I’ve been experimenting with AgX, Khronos PBR Neutral and a few other curves to see how they behave differently, while looking for a workflow that doesn’t require replacing the rest of Unreal’s post-processing pipeline.

I’ll share some comparisons once the forum lets me upload images :slight_smile:

In the meantime, I’d be interested to hear how others have approached this. Particularly, if you’ve tried changing the tone curve while preserving Unreal’s native grading controls.

worth separating the three places the tone curve actually gets applied, because most swap attempts break grading because they only replace one of them.

the default filmic tonemapper is baked into the mobile/new tonemappers selected via r.TonemapperOperator (and the legacy Film tonemapper before it), while PostProcess materials run after that stage, so a PPM “replacement” is really a re-map of an already tonemapped image, which is why it fights the grading. SceneViewExtensions are the cleanest hook because they can sit before the tonemapper in the render graph, that is how most of the existing plugins do it under the hood.

on the grading side, color grading in UE is done in log space against the standard curve, and r.Color.Mid is what ties midgray through the whole chain. if your replacement curve maps midgray somewhere else, adjust r.Color.Mid to match so the CDO of the color grading panel still behaves. scene color chain assumptions: anything that samples the scene color LUT assumes the default curve shape, so a log-curve mismatch shows up as grading that feels wrong even when the curve looks right on a scope.

practical middle ground if you want a different look but keep the workflow: keep the engine tonemapper, apply your curve as a LUT-based post process material with your curve baked into a neutral LUT, and use the global color grading panels for adjustments on top. you lose the “true” custom curve but keep every grading tool working as expected.

Thanks, really useful breakdown!
I’ve been digging through the engine code around this and found a couple of things that seem to work a little differently:

  • From what I can see, the tone curve isn’t actually a separate pass. It’s part of the colour grading LUT build in PostProcessCombineLUTs.usf: white balance and grading happen first, then FilmToneMap, then the user Color Grading LUT.

  • The Replacing the Tonemapper slot was interesting too. It does get scene colour before the tonemapper, but using it seems to make Unreal skip the grading LUT build altogether. So it’s not so much that the material is running after the tonemapper, but that the normal grading path is no longer being applied.

  • I also had a look at the SceneViewExtension route. From what I’ve found, replacing the tonemapper there has a similar problem: once you take over that stage, quite a bit more of the post process chain becomes your responsibility. Going earlier avoids that, but then Unreal’s tonemapper still runs afterwards.

That’s why I’m currently looking at something much narrower: changing just the FilmToneMap call inside the LUT build and leaving everything around it alone. That way, white balance, PP Volume grading, Tone Curve Amount and user LUTs can continue through Unreal’s existing path.

Here’s one of the tests I’ve been using. Same input colors on both sides, increasing in intensity from left to right. Filmic is on the left, AgX Neutral on the right.

The red is probably the easiest one to follow. With Filmic it starts shifting towards orange/yellow as intensity increases. AgX keeps the hue much more stable as it rolls towards white. You can see similar differences in the other saturated colors too.

Still testing this across engine versions and with different curves, but so far it’s been an interesting rabbit hole :slight_smile: