Skip to content

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:

  1. Saturation — lerp toward luminance
  2. Contrast — pivot around 0.5
  3. Gamma — power curve on midtones
  4. Gain — linear multiply
  5. 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.inir.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