Tonemapping
Summary: Project Eternal uses a custom AgX tonemapper replacing UE5's default ACES. This provides better color preservation in saturated highlights and supports the game's painterly chiaroscuro art direction with built-in color grading controls.
Table of Contents
Architecture Overview
┌──────────────────┐ ┌───────────────────┐ ┌──────────────┐
│ HDR Scene Color │────▶│ AgX Tonemapper │────▶│ Final Output│
│ (PostProcess │ │ (Replacing │ │ (sRGB) │
│ Input0) │ │ Tonemapper) │ │ │
└──────────────────┘ │ │ └──────────────┘
│ • Inset Matrix │
│ • Log2 Encoding │
│ • Sigmoid Curve │
│ • Outset Matrix │
│ • Color Grading │
│ • Dithering │
└───────────────────┘
| Design Decision |
Rationale |
| Replacing Tonemapper |
Full control over tone curve; UE5 color grading bypassed |
| Built-in color grading |
UE5 Global color grading only works with built-in tonemapper |
| Triangular-PDF dither |
Eliminates banding in dark scenes at negligible cost |
| RGBA16F scene buffer |
Prevents precision loss in low-light environments |
Why AgX Over ACES?
| Aspect |
ACES |
AgX |
| Saturated highlights |
Hue shifts toward yellow/magenta |
Preserves hue |
| Shadow detail |
Crushed aggressively |
Graceful rolloff |
| Overall character |
Contrasty, filmic |
Neutral, art-directable |
| Painterly suitability |
Fights muted palettes |
Complements earth tones |
AgX's neutral base allows the color grading controls to define the mood entirely, aligning with the GDD's "dramatic chiaroscuro with muted earth tones" directive.
Signal Flow
HDR Linear Input
│
▼
┌─────────────────┐
│ Inset Matrix │ sRGB linear → AgX working space
└────────┬────────┘
▼
┌─────────────────┐
│ Log2 Encoding │ Compress to [-12.47, 4.03] EV range → normalize to 0..1
└────────┬────────┘
▼
┌─────────────────┐
│ Sigmoid Curve │ 6th-order polynomial approximation of AgX S-curve
└────────┬────────┘
▼
┌─────────────────┐
│ Outset Matrix │ AgX working space → sRGB linear
└────────┬────────┘
▼
┌─────────────────┐
│ Color Grading │ Saturation → Contrast → Gamma → Gain → Shadow Crush
└────────┬────────┘
▼
┌─────────────────┐
│ Dithering │ Triangular-PDF noise, 1/255 amplitude, per-frame animated
└────────┬────────┘
▼
Linear Output
(engine applies sRGB)
Color Grading Controls
Since BL_ReplacingTonemapper bypasses UE5's built-in color grading, all grading is handled inside the shader via material parameters.
| Parameter |
Default |
Range |
Purpose |
Gain |
0.90 |
0.5–1.5 |
Overall brightness multiplier |
Gamma |
0.95 |
0.5–2.0 |
Midtone curve (lower = darker mids) |
Saturation |
0.90 |
0.0–2.0 |
Color intensity (0 = grayscale) |
ShadowMax |
0.02 |
0.0–0.15 |
Shadow crush threshold (higher = more black) |
Contrast |
1.05 |
0.5–2.0 |
S-curve strength around 0.5 midpoint |
Grading Order
The order matters — changing it produces different results:
- Saturation — lerp toward luminance
- Contrast — pivot around 0.5
- Gamma — power curve on midtones
- Gain — linear multiply
- Shadow Crush — remap below threshold to black
Art Direction Targets
| Look |
Gain |
Gamma |
Saturation |
ShadowMax |
Contrast |
| Chiaroscuro (default) |
0.90 |
0.95 |
0.90 |
0.02 |
1.05 |
| Deep dungeon |
0.75 |
0.85 |
0.80 |
0.06 |
1.15 |
| Overworld daylight |
1.00 |
1.00 |
0.95 |
0.01 |
1.00 |
Post Process Setup
| Setting |
Value |
Reason |
| Material Domain |
Post Process |
— |
| Blendable Location |
Replacing Tonemapper |
Replaces ACES entirely |
| Metering Mode |
Manual |
Consistent artistic exposure |
| Exposure Compensation |
0.0 |
Neutral; darken via Gain instead |
| Apply Physical Camera Exposure |
Off |
Non-physical light values for artistic control |
The material is applied via an Unbound Post Process Volume with Infinite Extent to affect the entire scene.
Scene Color Buffer
| Setting |
Value |
Purpose |
r.SceneColorFormat |
2 (RGBA16F) |
16-bit precision prevents banding |
Set in Config/DefaultEngine.ini under [/Script/Engine.RendererSettings]. The default R11G11B10 format (4 bytes/pixel) lacks precision for dark scenes. RGBA16F (8 bytes/pixel) eliminates banding at a minor VRAM cost (~16MB at 4K).
Requires editor restart to take effect.
Source References
| Component |
Location |
| AgX HLSL reference |
Shaders/AgXTonemapper.ush |
| Post-process material |
Content/MaterialLibrary/Postprocessing/M_PP_AgX.uasset |
| Material instance |
Content/MaterialLibrary/Postprocessing/MI_PP_AgX.uasset |
| Scene color format |
Config/DefaultEngine.ini → r.SceneColorFormat=2 |
- Post Process Volumes in level maps control per-area overrides
- GDD Art Direction (Section 4.4) defines the painterly chiaroscuro target
Recent Changes
| Date |
Change |
Impact |
| 2026-03-09 |
Added AgX tonemapper replacing ACES |
All scenes use new tone curve |
| 2026-03-09 |
Scene color buffer → RGBA16F |
Eliminates color banding |