Rendering in Retrospect · #9 of 12
Post-Processing Is Where Taste Lives
Retrospective — written in 2026, looking back.
The Last Ten Percent That Reads as Ninety
Most of a renderer is correctness work. A shadow map either leaks or it doesn't. A normal is either in the right space or the lighting is visibly wrong. Post-processing is different. By the time an image reaches the post chain, the geometry is resolved, the lighting is integrated, and what remains is a high dynamic range buffer and a series of choices. None of those choices has a single right answer, and that is exactly why this stage is where a personal project starts to look like yours rather than like a tutorial.
For years I treated post-processing as polish to add at the end — a tonemapper, maybe some bloom if there was time. Working on small personal scenes changed that view. A grey, physically plausible render and a memorable image can come from the exact same scene data. The difference is almost entirely in what happens after the main pass.
Work in HDR Until the Very End
The foundational rule is boring and non-negotiable: keep the pipeline in floating point as long as possible, and tonemap once, late.
If you tonemap early — or worse, let individual materials output display-ready values — you throw away the information every later effect needs. Bloom needs to know which pixels are actually bright, not which ones look bright after compression. Color grading needs headroom in the highlights and footroom in the shadows to move things around without banding.
In my personal projects the scene renders into an RGBA16F target. Lighting, emissives, and sky all accumulate there in linear space, in values that can go well above 1.0. A sun-lit white surface might sit at 3 or 4. A deliberate emissive accent might be 10 or 20. Those ratios matter. They are what separate "this glows" from "this is just a light color."
Only at the end of the chain do I convert to display space. That discipline alone fixes a whole category of bugs: washed-out bloom, grading that crushes everything to mud, and vignettes that band in the corners.
Tonemapping Is a Curve, Not a Checkbox
Tonemapping is the function that squeezes that HDR range into what a display can show. Reinhard, x / (1 + x), is simple and never clips, but it desaturates highlights and feels soft. Filmic curves, like ACES, roll off highlights more gracefully, preserve contrast in the mid-tones, and shift bright saturated colors toward white in a way that resembles film response.
There is no universally correct choice here. ACES looks great for a scene with a bright sky and skin tones or foliage. For a stylized scene with deliberate, saturated emissive colors — a magic effect, a neon sign in a personal diorama — ACES can mute exactly the colors I chose on purpose. Sometimes I use a simpler curve for those projects, or I blend: ACES for the world, with emissive accents added after tonemapping at controlled intensity so they keep their hue.
Exposure sits before the curve and is the most powerful knob of all. Half a stop up or down changes the mood more than most grading work. I now expose the scene first, on a neutral grey test room, before I judge any material. Grading a badly exposed image is just compensating.
// Simple illustrative tonemap stage — linear HDR in, display-ready out.
// Exposure first, then the curve, then gamma handled by the output surface.
vec3 TonemapACES(vec3 x) {
const float a = 2.51;
const float b = 0.03;
const float c = 2.43;
const float d = 0.59;
const float e = 0.14;
return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);
}
vec3 ApplyPost(vec3 hdr, float exposure) {
vec3 exposed = hdr * exposure;
return TonemapACES(exposed);
}
That snippet is deliberately minimal. In a real chain it is one step in the middle, not the whole story.
Bloom: Threshold With Intent
Bloom is the effect beginners overuse and experienced people underuse in public while using carefully in private. The technique is standard: extract pixels above a threshold, blur them at multiple scales, add them back. The taste is in the threshold and the strength.
A hard luminance threshold pops. A soft knee — where contribution ramps in smoothly around the threshold — feels much more natural:
// Soft-knee bright pass (conceptual)
float SoftThreshold(float lum, float threshold, float knee) {
float range = clamp((lum - threshold + knee) / (2.0 * knee), 0.0, 1.0);
return range * range * max(lum - threshold, 0.0) / max(lum, 1e-4);
}
Two lessons from personal experiments stuck with me. First, bloom should be driven by luminance ratios set at the material level. If I want a window to bloom and a white wall not to, I do not lower the threshold until both glow — I raise the window's emissive intensity in HDR. Second, radius matters more than strength. A wide, faint halo reads as atmosphere and light scatter. A tight, strong halo reads as a filter. Most of the time, the wide and faint version is what I actually wanted.
Color Grading: Small Moves, Late
Grading is where taste becomes most explicit: lift, gamma, gain; shadow / midtone / highlight wheels; a 3D LUT baked from a look developed in an external tool. My rule now is to make grading moves small and to make them late — after tonemapping, in display-referred space, where the wheels behave the way colorists expect.
The mistake I kept making early on was grading to fix lighting. If shadows were blue because my ambient term was wrong, pushing orange into the shadows in the grade would sort of fix one view and break three others. Fix the lighting in the lighting. Grade for mood: a slightly cool shadow and warm highlight for late-afternoon melancholy, a desaturated, contrasty look for a ruined interior. A LUT is a wonderful tool for this because it makes the look portable and inspectable — I can apply the same LUT to a test chart and see exactly what it does to skin tones, saturated reds, and near-blacks before I trust it on a scene.
Vignette and Grain: The Dishonest Effects I Keep
Vignette and film grain do nothing physically correct, and I use both anyway — carefully.
A vignette darkens the corners and quietly directs the eye to the center. At a strength where you can measure it but not see it, it helps almost any composition. The moment a viewer can point at it, it is too strong.
Film grain does two useful things. It dithers the image, hiding banding in skies and dark gradients that would otherwise show ugly steps. And it adds temporal life — a still render can feel dead in a way a slightly noisy one does not. The key is to scale grain with luminance: more in the shadows, almost none in bright highlights, and to animate it per frame with a hash of pixel position and frame index rather than a static texture, which would look like dirt on a lens.
Order of Operations Is the Whole Game
If there is one concrete takeaway from this post, it is the order:
- 1. Render scene to HDR, linear.
- 2. Any effect that needs true brightness — bloom extraction happens here, from HDR.
- 3. Exposure and tonemapping.
- 4. Color grade / LUT in display space.
- 5. Bloom composite decisions get finalized relative to that — in my chain bloom is added before tonemapping so bright halos tonemap naturally, rather than sitting on top as raw additive white.
- 6. Vignette, grain, and dither last, just before output.
Swap steps and the artifacts are predictable. Add bloom after tonemapping and halos clip to flat white. Apply grain before grading and the grade amplifies it unevenly. Vignette before bloom and the corners bloom as if the lens were dirty. Writing this order down and pinning it above my desk did more for my images than any single effect.
Taste Is Testable
The comforting myth is that taste is innate and unteachable. What personal projects taught me is that taste in post is mostly a comparison discipline. Render the same scene three ways. Look at them the next morning, not in the moment. Keep a folder of reference images — photographs, paintings, film stills — and try to match a feeling, not a pixel value. Notice which version you keep opening.
Correctness gets an image to plausible. Taste, exercised one deliberate choice at a time in exposure, curve, threshold, and grade, is what gets it to memorable.