Computer Graphics
Post-Processing: Bloom, DoF, Motion Blur, Tone Mapping
Cyberpunk 2077 sold 13 million copies in 10 days in 2020 - a PC record. Critics praised its cinematic visuals. The same polygons with and without post-processing look like different games. HDR Bloom, Bokeh DoF, and ACES tone mapping are what separate a tech demo from an experience.
- Cyberpunk 2077: HDR Bloom + ACES tone mapping make neon lights glow without blowing out on night streets
- God of War: cinematic hexagonal bokeh DoF on portrait scenes running in real time
- Unreal Engine 5: Temporal Super Resolution uses the motion blur velocity buffer to upscale 4K from 1080p
- Horizon Forbidden West: per-object motion blur for characters via a separate skinned velocity pass
Bloom: Why Bright Lights Glow
A camera sensor cannot capture the full dynamic range of a sunny day - 100 000:1 versus 255:1 for an SDR screen. Bloom does not 'add glow' - it simulates optical scattering inside the lens that occurs precisely when the scene is in HDR.
**Bloom pipeline:** 1) render scene to HDR buffer (16f RGB); 2) threshold pass - extract pixels with luminance > 1.0; 3) downscale to 1/2, 1/4, 1/8, 1/16; 4) separable Gaussian blur (horizontal then vertical) at each mip; 5) additive upsample back to full resolution; 6) combine with original before tone mapping. Separable Gaussian (two 1D passes) costs O(n) instead of O(n²) for a 2D kernel.
**Dual Kawase vs Gaussian.** Unreal Engine 4 uses Dual Kawase blur instead of Gaussian - it requires half the texture samples for a similar result. Each downscale pass reads 4 neighboring pixels at +0.5 offset, producing a Gaussian-like blur without an explicit kernel.
Why is Gaussian blur for Bloom split into two passes - horizontal then vertical?
Depth of Field: Blur From the Circle of Confusion
In a real camera, objects outside the focal plane project not to a point but to a disk - the Circle of Confusion (CoC). The larger the disk, the blurrier the object. GPU DoF reproduces this geometry using depth from the G-buffer.
**Gather vs Scatter DoF.** Gather: each pixel collects contributions from its CoC radius (shown above). Scatter: each bright pixel projects bokeh disks onto neighbors - physically more accurate but requires vertex shader instancing with alpha blend and no depth test. Unreal Engine 5 uses Scatter for bright light sources and Gather for the background.
What is the Circle of Confusion (CoC) in depth of field?
Motion Blur: Smear From the Velocity Buffer
Without motion blur, 30 fps animation looks choppy even on a fast GPU - every frame is too sharp. A real camera integrates light over the shutter interval (1/60 s), smearing moving objects. The GPU reproduces this in a single post-processing pass.
**Screen-Space Velocity Buffer.** During the G-buffer pass the vertex shader computes the difference between current and previous NDC position for each pixel: `velocity = (currentNDC - prevNDC) * 0.5`. The post-processing shader reads the velocity vector and takes N samples along it, averaging the color. Sample counts: 8-16 for games, 32+ for cinematic rendering.
**Per-object vs camera motion blur.** A velocity buffer derived from NDC delta automatically captures both camera and object motion. But skinned characters need velocity per bone. Unreal Engine maintains a separate skinned velocity buffer for Skeletal Mesh - an extra geometry pass - which is critical for correct blur on animated characters.
How is the velocity buffer used for motion blur?
Tone Mapping: From HDR to What the Screen Shows
After Bloom, DoF, and Motion Blur the HDR buffer holds luminance values from 0 to 10 000 nits. A display shows 0-255. Tone mapping is a non-linear compression of that range preserving detail in both shadows and highlights. Without it the bright region blows out to white and the dark region crushes to black.
**ACES and Display-P3.** ACES (Academy Color Encoding System) was created by the film industry in 2014. Epic Games switched Unreal Engine to ACES in UE4.15. Modern iPhones use Display-P3 (DCI-P3 with D65) - a color space 25% wider than sRGB. To render correctly, the target color space must be known before tone mapping.
Tone mapping and gamma correction are the same thing
Tone mapping compresses HDR to LDR with a perceptual S-curve. Gamma correction converts linear space to sRGB (power 1/2.2). They are separate operations applied in sequence: tone mapping first, then gamma.
Confusing them results in blown-out highlights with no detail or flat desaturated shadows. ACES followed by gamma gives a cinematic result; Reinhard without gamma gives a flat, washed-out image.
Why is tone mapping needed and where does it appear in the pipeline?
Key ideas
- Bloom: HDR threshold -> downscale mip chain -> separable 1D Gaussian (O(n)) -> additive upsample
- DoF: Circle of Confusion from G-buffer depth -> gather N samples over CoC radius (hexagonal bokeh)
- Motion Blur: velocity = current NDC - previous NDC -> N samples along vector -> average
- Tone Mapping: last step - compress HDR to LDR with S-curve (ACES) -> gamma correction -> display
- Pipeline order: G-buffer -> HDR lighting -> Bloom -> DoF -> Motion Blur -> Tone Mapping -> gamma
Related topics
Post-processing completes the HDR pipeline started in Deferred Rendering.
- Deferred Rendering — G-buffer with depth and velocity is the data source for all post-effects
- GPU Graphics Pipeline — Fullscreen quad render target is the mechanism behind every post-processing pass
- Terrain and LOD — Terrain renders into the same HDR buffer and passes through bloom and tone mapping
Вопросы для размышления
- Why must Bloom be applied before tone mapping and not after? What changes if the order is swapped?
- How does Temporal Anti-Aliasing use the same velocity buffer as motion blur for a completely different purpose?
- ACES is a film-industry standard. What trade-offs does it have compared to Reinhard for games with bright HUD elements?
Связанные уроки
- cg-14 — Deferred Rendering produces the G-buffer that all post-processing effects sample from
- cg-08 — Compute shaders and render targets are the mechanism behind every fullscreen pass
- cg-16 — Terrain LOD renders into the same HDR buffer and passes through the same post pipeline
- dsp-04 — Bloom and DoF are convolution filters: Gaussian and bokeh kernels from signal processing theory
- arch-09-cache