# HDR & tone mapping > **Step 20** — Opt-in HDR rendering with tone mapping. ## Principle By default, WSG renders **directly to the swapchain** in 8-bit sRGB. This is fine for simple scenes, but the moment you want **bloom** or physically plausible intensities, you hit the ceiling: 8-bit clamps everything to [0,1] before any post-process can run. When HDR is enabled, the scene is first rendered into an **offscreen float texture** (`Rgba16Float`, full window resolution), where values are unbounded (no clamping). The **tone mapping** pass then compresses the HDR signal into [0,1] sRGB for the swapchain. ``` Without HDR (default) With HDR (.with_hdr(ToneMapper::Aces)) ───────────────────── ────────────────────────────────────── Scene ──────────→ Swapchain Scene ──→ HDR texture (Rgba16Float) (8-bit sRGB, clamped) │ (float, unbounded) ▼ Tone Mapping (ACES / Reinhard) │ ▼ Swapchain (sRGB) ``` ## Enabling ```rust use wsg_lib::prelude::*; let app = AppBuilder::new() .with_hdr(ToneMapper::Aces) // or ToneMapper::Reinhard .build() .await?; ``` - **Without** `with_hdr`: the pipeline is untouched, **zero cost** (no extra texture, no extra pass). - **With** `with_hdr`: one extra full-res texture + one fullscreen pass per frame. Negligible cost on any discrete GPU; moderate on an integrated one (full-res read+write). ## Tone mappers | Curve | Characteristics | |-------|----------------| | `Aces` | **Default.** Filmic look, good highlight roll-off, slightly desaturated in the shadows. The standard for games and engines. | | `Reinhard` | Simple `c / (1 + c)`. Neutral and fast, but highlights "washed out" (the curve saturates quickly). | > The `demo` example starts in **LDR** (no HDR): the sphere's emissive intensity of 3.0 is > clamped to 1.0 — it looks "burnt" but no glow. Pressing a key enables HDR+ACES and the > highlight rolls off gracefully. ## Exposure Runtime-adjustable since Step 22: initialize with `AppBuilder::with_exposure(…)`, adjust with `app.set_exposure(…)` (multiplicative, clamped to [0.01, 10.0] — only active when HDR is on). See [Emissive + Exposure](../lights/emissive-exposure.md). ## Interaction with other features | Feature | Behavior under HDR | |---------|-------------------| | **Shadows** | Unchanged — the shadow pass still writes depth; the color pass just targets the HDR texture instead of the swapchain. | | **Fog** | Applied **before** tone mapping (inside the main pass). The fog color must be chosen in linear space, consistent with the tone curve. | | **MSAA** | Compatible — the HDR texture becomes the multisample render target and is resolved before tone mapping. | | **Bloom** | **Requires** HDR. Without it, bloom is ignored (warning). | ## Cost and non-regression - No HDR (default): nothing is allocated, nothing is run. The swapchain is targeted directly. - HDR enabled: one extra `Rgba16Float` texture + one fullscreen pass (tone mapping). If bloom is also enabled, three more half-res textures and a few more passes (see the bloom page). - The `demo` example shows both paths side by side: the LDR start (emissive clamped) and the HDR+ACES mode (highlight roll-off). ## Limitations - No **auto-exposure** (histogram-based). Exposure is fixed at build time (the demo hardcodes 1.0). - No **DITHERING** on the output: banding may appear in smooth gradients near black (sRGB 8-bit ceiling). ## See also - [Shadows](../lights/shadows.md) - [GPU-driven rendering](../cameras/gpu-driven.md) - [Examples](../examples.md)