DEV Community

GameDevToolLab
GameDevToolLab

Posted on

HDR in Unity URP: Bloom, Tone Mapping, and HDR Display Output

Introduction

HDR, Emission, Bloom, Exposure, and Tonemapping are related but different features. Player Settings adds another: HDR Display Output.

Separate brightness during rendering from HDR display output, using an SDR-monitor experiment, diagnostics, and custom-rendering examples.

Sections 1–5 cover internal HDR; 6–8 cover output. Skip advanced sections 9–10 for troubleshooting in section 11 as needed.

Scope: Unity 6.3 LTS (6000.3), URP 17.3, English UI. Documentation review date recorded in the Japanese source: September 15, 2026. This translation does not revalidate every reference.1

Untested examples: Unity compilation, scene execution, Player builds, HDR-display testing, and benchmarking have not been performed for this article. Observation prompts are not measured results.

1. Two meanings of HDR

HDR means High Dynamic Range. In Unity, distinguish these two uses:2

HDR rendering HDR display output
Purpose Preserve brightness during rendering Use an HDR display path
Controls Camera, shader, buffer, post-processing Player, OS, GPU, display
HDR monitor? Not for internal processing Yes, with compatible output

HDR rendering can end in SDR output. Games for SDR monitors can preserve HDR internally and map it to the display range afterward.2

High Definition Render Pipeline (HDRP) is not required for HDR. URP supports it, and HDRP also needs output configuration.34

2. Why values above 1 matter

A white wall is not an emissive sign

These grayscale shader outputs illustrate scene values, not measured luminance:

White surface:     ( 1,  1,  1)
Emissive surface:  ( 4,  4,  4)
Strong emission:   (16, 16, 16)
Enter fullscreen mode Exit fullscreen mode

Clamping them to 0–1 makes all three (1, 1, 1). Later processing cannot recover the difference.

An HDR buffer preserves differences for Bloom and later output conversion. HDR retains information; it does not simply brighten the image.2

Tone mapping is not clipping

Tone mapping remaps brightness to the output range. For example:

y = x / (1 + x)
Enter fullscreen mode Exit fullscreen mode
Input x Clamped to 0–1 Result of the formula
1 1 0.500
4 1 0.800
16 1 0.941

This preserves differences within 0–1. It is not Unity's Neutral or ACES formula. Choose URP's operator in the Tonemapping Volume Override.5

3. Linear, HDR, gamut, and bit depth

Concept What it describes
Linear / Gamma How lighting and color values relate numerically
HDR / SDR Brightness range
Color gamut Represented/encoded color range
Bit depth / format Precision and numerical range

Linear does not enable HDR output. 10-bit storage need not be HDR, and HDR does not make assets wide-gamut.67

Set Project Settings > Player > Other Settings > Rendering > Color Space to Linear. This changes lighting and blending in existing Gamma projects; compare and readjust their appearance.89

Do not disable sRGB (Color Texture) everywhere. Color textures and data masks differ. Disabling it skips sRGB-to-Linear sampling conversion; choose according to the data.10

4. Compare HDR on an SDR monitor

Disable HDR display output initially to isolate internal HDR from OS/display settings.

4.1 Check the active URP Asset

An asset assigned to the current Quality level overrides the default. Identify the active asset with GraphicsSettings.currentRenderPipeline.11

Enable its HDR setting and set Post-processing > Grading Mode to High Dynamic Range. Grading Mode controls grading, not display output.12

4.2 Configure the camera and Volume

Use one Base Camera without a target RenderTexture. Enable Output > HDR Rendering and Rendering > Post Processing in its Inspector.13

Create GameObject > Volume > Global Volume with a new Profile and Weight 1. Include its Layer in the camera's Volume Mask. Disable other Volumes and custom Renderer Features.1413

Add Bloom and Tonemapping. Enable each edited setting's override checkbox:

Override Setting Comparison value
Bloom Threshold 2.0
Bloom Intensity 0.5
Bloom Scatter 0.7
Tonemapping Mode None initially

These are examples, not defaults or universal recommendations. Leave other color/exposure adjustments off.

Bloom Threshold uses gamma-space brightness. Threshold 2 does not directly select Linear shader values above 2.15

URP includes Volume-based post-processing; do not mix in Post Processing Stack v2.16

4.3 Build an HDR Unlit Shader Graph

Create a Universal Unlit Shader Graph to avoid lighting effects. Expose a Color property named HDR Color, Mode HDR, connected to Fragment Base Color. Keep Surface Type Opaque.1718

HDR Color property
    Mode: HDR
        |
        +-- Fragment / Base Color
Enter fullscreen mode Exit fullscreen mode

Create three materials, with Alpha 1. Enter RGB using the HDR picker's float display:

Material RGB Intensity from white
A (1, 1, 1) 0
B (4, 4, 4) +2
C (16, 16, 16) +4

Each Intensity step doubles RGB: +2 means ×4, +4 means ×16—not ×4. Reopening the picker can change its color/intensity decomposition without changing stored RGB.19

Assign A, B, C to Cubes at (-1.5, 0, 0), (0, 0, 0), (1.5, 0, 0). Use camera position (0, 0, -6), rotation (0, 0, 0), Orthographic Size 2, a dark solid background, and 16:9 Game View.

4.4 Change one variable at a time

These are observation prompts, not results recorded in Unity:

Configuration (Bloom values are Intensity) What to examine
HDR on, Bloom 0, Tonemapping None Does SDR hide differences in the white surfaces?
HDR on, Bloom 0.5, Tonemapping None Do similar whites produce different glow?
HDR on, Bloom on, Neutral How do highlights and surroundings change?
Same setup, HDR Rendering off How does losing HDR differences affect Bloom?

Look for different values behind apparently identical whites, not just glow.

Bloom also works on 0–1 images with a low Threshold. HDR distinguishes brighter-than-white values.15

5. Separate Emission, Bloom, exposure, and tone mapping

This conceptual flow is not an exact render-pass schedule:22021

Shaders and lighting
    -> HDR scene color
    -> Image effects such as Bloom
    -> Post Exposure and color adjustments
    -> Tone mapping
    -> SDR or HDR display output
Enter fullscreen mode Exit fullscreen mode

Emission is not Bloom

Emission represents a Lit material's self-emission; Bloom spreads bright image regions. Increasing Emission does not enable Bloom.2215

Emission does not guarantee nearby illumination; check the GI setup and method. An additional Light can illuminate an emissive sign's surroundings.22

This class-field declaration enables an HDR color picker, not camera HDR:23

[SerializeField, ColorUsage(false, true)]
private Color emissionColor = Color.white;
Enter fullscreen mode Exit fullscreen mode

Post Exposure comes after HDR effects

Post Exposure in Color Adjustments runs after HDR effects, before tone mapping. Raising it is not equivalent to raising Bloom's input.20

Choose the emitter, Bloom extraction, or processed-image exposure control according to your goal.

Compare Neutral and ACES by appearance

Neutral minimizes hue/saturation changes. ACES approximates the reference ACES tone mapper, with stronger contrast and hue/saturation changes.5

For stylized emission, compare Neutral with ACES. Choose the intended look; ACES is not automatically better.

6. Enable HDR display output

URP HDR output requires Linear, an HDR-capable environment, and HDR in the active URP Asset. Check Player Other Settings:213

Setting Purpose
Allow HDR Display Output Permit HDR output
Use HDR Display Output Use HDR on the main display

A monitor alone is insufficient: check OS HDR settings, GPU, driver, and Graphics API.7

Start Windows testing with a D3D12 Player. Unity 6.3 URP also lists D3D11 and Vulkan. D3D11 HDR is Player-only, not available in the Editor or exclusive fullscreen. Use borderless fullscreen.21

HDR output still maps to the display range rather than sending scene values unchanged.21

7. Inspect HDR output in code

7.1 Read output and request status

Read HDROutputSettings.main: available includes the main display and project settings; active reports current HDR output. Availability is not a monitor-hardware-only test.2425

HDRModeChangeRequested reports a pending request, not current output. This diagnostic blocks further requests while one is pending; the caller checks completion later.2627

Save HdrOutputProbe.cs and attach it to a GameObject. It uses UnityEngine only. The source revision checked Unity 6.3 API/C# declarations, not compilation or execution. HDRModeChangeRequested is a property, without ().2829

using UnityEngine;

[DisallowMultipleComponent]
public sealed class HdrOutputProbe : MonoBehaviour
{
    private void Start()
    {
        LogStatus();
    }

    [ContextMenu("HDR/Log Status")]
    public void LogStatus()
    {
        var hdr = HDROutputSettings.main;
        if (hdr == null)
        {
            Debug.LogWarning("Could not retrieve HDR output settings.", this);
            return;
        }

        bool available = hdr.available;
        bool active = hdr.active;
        Debug.Log(
            $"ColorSpace={QualitySettings.activeColorSpace}, " +
            $"HDR available={available}, active={active}",
            this);

        // Do not read request status or luminance data when HDR is unavailable.
        if (!available)
        {
            Debug.Log("Skipped HDR details because HDR is unavailable.", this);
            return;
        }

        Debug.Log(
            $"API automaticHDRTonemapping={hdr.automaticHDRTonemapping}, " +
            $"HDRModeChangeRequested={hdr.HDRModeChangeRequested}",
            this);

        if (!active)
        {
            Debug.Log("HDR output is inactive. Skipped luminance data.", this);
            return;
        }

        // These are API values, not the effective settings of the URP Volume.
        Debug.Log(
            $"API paperWhiteNits={hdr.paperWhiteNits:F1} nits, " +
            $"minToneMapLuminance={hdr.minToneMapLuminance} nits, " +
            $"maxToneMapLuminance(10%)={hdr.maxToneMapLuminance} nits, " +
            $"maxFullFrameToneMapLuminance(100%)=" +
            $"{hdr.maxFullFrameToneMapLuminance} nits",
            this);
    }

    [ContextMenu("HDR/Request On")]
    public void RequestOn() => RequestMode(true);

    [ContextMenu("HDR/Request Off")]
    public void RequestOff() => RequestMode(false);

    private void RequestMode(bool enable)
    {
        if (!Application.isPlaying)
        {
            Debug.LogWarning("Call this method while the application is playing.", this);
            return;
        }

        var hdr = HDROutputSettings.main;
        // Check availability for both enabling and disabling HDR.
        if (hdr == null || !hdr.available)
        {
            Debug.LogWarning(
                "Cannot request an HDR output mode change in this environment.",
                this);
            return;
        }

        // Do not send another request, including a reversal, while one is pending.
        if (hdr.HDRModeChangeRequested)
        {
            Debug.LogWarning("An HDR output mode change request is still pending.", this);
            return;
        }

        if (hdr.active == enable)
        {
            Debug.Log($"HDR output is already in the requested state: {enable}", this);
            return;
        }

        hdr.RequestHDRModeChange(enable);
        Debug.Log(
            $"Requested HDR output change: {enable}. " +
            "Call LogStatus later to check the pending state and active output.",
            this);
    }
}
Enter fullscreen mode Exit fullscreen mode

It logs at startup. Use its context menu in Play Mode, or public methods from Player UI Buttons. Install only one HdrOutputProbe.cs, not both pasted and supplied copies.

7.2 Guard both on and off requests

RequestHDRModeChange is documented to throw when HDR is unavailable. false is not exempt. Both directions use hdr == null || !hdr.available.26

HDRModeChangeRequested and paperWhiteNits also document unavailable-environment exceptions. Read details after checking available, and luminance only when active.2730

Pending-request blocking and same-state skipping are sample policies, not mandatory Unity rules. Checking pending status first avoids treating an unfinished transition as complete.

available=true does not guarantee runtime switching everywhere. Returning from a request does not prove completion; call LogStatus() later to check pending status and active.2625

The sample lacks polling, timeouts, and disconnection recovery. Production UI needs availability rechecks, pending-state input restrictions, and completion updates. Save settings after confirming output, not logging a request.

7.3 Interpret API values correctly

API identifies HDR output settings, not photometer measurements or effective camera-Volume settings.2831

Log field Meaning or limitation
available Availability for the main display and project
active Current HDR state; compare against the request
HDRModeChangeRequested Pending change; read only when available
automaticHDRTonemapping Common API automatic tone-mapping state
paperWhiteNits Reference white for automatic mapping with active HDR
minToneMapLuminance Minimum input luminance retaining detail
maxToneMapLuminance(10%) Maximum input luminance retaining detail: 10% bright area
maxFullFrameToneMapLuminance(100%) Maximum input luminance retaining detail: entire screen bright

maxToneMapLuminance is not an unconditional display peak. (10%) and (100%) are log labels, not API names.28

paperWhiteNits is documented for automatic HDR tone mapping with active HDR output; reading it does not prove URP uses it. automaticHDRTonemapping=false does not imply that the URP Tonemapping Volume is disabled.303231

URP Volumes can override Paper White and luminance limits. Check output status in logs and artistic settings in the camera's Volumes. The probe does not change automatic tone mapping or Paper White.315

available=true, active=false means available but inactive. available=false proves neither hardware failure nor an SDR-only monitor. These examples are not captured logs.2425

8. Paper White and highlight limits

1 nit = 1 cd/m² is a luminance unit, not a relative shader color value.28

Paper White controls reference white, including UI white—not the display's maximum brightness.21

For illustration, ordinary white might be 200 nits and highlights reach 1000 nits; UI need not reach the maximum. These are not universal recommendations. Shader 1.0 is not inherently 1 or 200 nits; conversion and settings determine that relationship.31

HDR Tonemapping adds these controls. Both Neutral and ACES support Paper White.5

Setting Neutral ACES
Automatic white Detect Paper White Detect Paper White
Manual white Paper White Paper White
Brightness range Detect Brightness Limits, Min Nits, Max Nits ACES Preset: 1000, 2000, or 4000 nits

Disable Detect Paper White for manual white in either mode. Neutral exposes Min Nits/Max Nits when Detect Brightness Limits is off; ACES uses a preset. White controls overlap, but range controls differ.5

An ACES 1000-nit preset does not make UI white 1000 nits. Compare UI and emission, checking Inspector overrides rather than preset names.521

Display reports can be inaccurate. Consider user calibration across highlights, menus, subtitles, and loading screens.5

9. Advanced: preserve HDR data

9.1 Avoid saturating HDR color

saturate in HLSL and Shader Graph clamps to 0–1: useful for masks, destructive for HDR color before tone mapping.33

// Expressions inside a shader, not a complete Shader file.
float3 hdrColor = baseColor * intensity;

// Outputting this as scene color loses differences above 1.
float3 clippedColor = saturate(hdrColor);
Enter fullscreen mode Exit fullscreen mode

Treat masks and interpolation weights separately from colors that need an HDR range.

9.2 Avoid duplicate color conversions

New Shader Graph HDR Color Nodes use Linear; old graphs may retain compatibility behavior. Unlike color textures, they may not need conversion. Check the input space before adding .linear or pow.34

9.3 Choose an appropriate RenderTexture

Intermediate RenderTexture clipping is irreversible. These formats describe raw scene-linear color before display encoding, not final HDR signals:35

GraphicsFormat Bits/pixel Raw values above 1 Alpha
R8G8B8A8_UNorm 32 No Yes
B10G11R11_UFloatPack32 32 Yes, positive values No
R16G16B16A16_SFloat 64 Yes Yes

UNorm is not always SDR: HDR10 can use PQ-encoded 10-bit UNorm. Storage format and signal encoding both matter.7

URP's 64-bit HDR Precision increases precision and bandwidth cost. HDR Alpha Processing requires 64-bit; 32-bit HDR does not necessarily include alpha.12

A target RenderTexture hides some camera HDR controls and determines target settings. Camera HDR alone does not guarantee a suitable format.13

9.4 Match the custom-pass stage

Post-processed HDR output may not mean scene-linear color. At or after AfterRenderingPostProcessing, account for gamut and luminance conversion.31

For post-processing overlays or custom UI, check white levels and duplicate conversion. A float RenderTexture cannot prevent a doubled output transform.36

9.5 EXR cannot recover discarded values

Unity can encode EXR, but the source must retain HDR data. EXR cannot restore values already lost to tone mapping or clamping.37

10. Advanced: memory and performance

HDR need not double color-buffer storage: 32-bit HDR exists. At fixed resolution, 32 to 64 bits doubles an uncompressed buffer's raw size.35

At 3840 × 2160:

32 bits/pixel:
3840 × 2160 × 4 bytes / 1024² ≈ 31.64 MiB

64 bits/pixel:
3840 × 2160 × 8 bytes / 1024² ≈ 63.28 MiB
Enter fullscreen mode Exit fullscreen mode

These theoretical sizes cover one color buffer, not depth, MSAA, copies, history, or alignment. They are not measured application memory.

Compare HDR, precision, resolution, and Bloom separately, including high-quality Bloom filtering's added cost and benefit.15

On the same hardware and resolution, toggle HDR, 32/64-bit precision, and Bloom individually. Record GPU time and appearance.

11. Troubleshooting

These are starting points, not diagnoses:

Symptom Check first
No visible HDR difference Above-1 values and effects that reveal them
Emission without glow Post Processing, Volume Mask, Bloom override/Intensity
Ordinary whites glow Emitter values and Bloom Threshold
HDR display receives SDR OS/Player, Linear, Graphics API, available/active
UI too bright Paper White, composition stage, output conversion
HDR lost after a custom effect saturate, buffer formats, repeated tone mapping
Intermediate alpha lost Target format, HDR Precision, Alpha Processing
Editor/Player mismatch Quality pipeline asset and Graphics API constraints

Use Window > Analysis > Render Pipeline Debugger > Lighting > HDR Debug Mode to inspect gamut and regions above Paper White, rather than relying only on the final image.38

Conclusion

Track where brightness is created, preserved, and converted for display. Compare 1, 4, and 16 with internal HDR, Bloom, and tone mapping; then inspect display output and Paper White.

Separate rendering from display-path problems, ordinary white from highlights, and always check SDR too.


  1. Unity Manual, Universal RP package — Unity 6.3. ↩

  2. Unity Manual, Introduction to high dynamic range (HDR). ↩

  3. Unity Manual, Enable HDR Output in URP. ↩

  4. Unity HDRP 17.3 documentation, HDR Output. ↩

  5. Unity Manual, Tonemapping Volume Override reference for URP. ↩

  6. Unity Manual, Color spaces. ↩

  7. Microsoft Learn, Use DirectX with Advanced Color on high/standard dynamic range displays. See the distinctions between storage formats and encoding, and between bit depth, gamut, and dynamic range. ↩

  8. Unity Manual, Set the project color space. ↩

  9. Unity Manual, Differences between linear and gamma color spaces. ↩

  10. Unity Manual, Disable sRGB sampling for a texture. ↩

  11. Unity Scripting API, QualitySettings.renderPipeline, GraphicsSettings.currentRenderPipeline. ↩

  12. Unity Manual, URP Asset reference. ↩

  13. Unity Manual, Camera component reference for URP. ↩

  14. Unity Manual, Add post-processing in URP. ↩

  15. Unity Manual, Bloom Volume Override reference for URP. ↩

  16. Unity Manual, Post-processing in URP. ↩

  17. Unity Shader Graph 17.3 documentation, Property Types. ↩

  18. Unity Shader Graph 17.3 documentation, Your first Shader Graph. ↩

  19. Unity Manual, HDR color picker reference. ↩

  20. Unity Manual, Color Adjustments Volume Override reference for URP. ↩

  21. Unity Manual, HDR Output in URP. ↩

  22. Unity Manual, Lit shader reference for URP. ↩

  23. Unity Scripting API, ColorUsageAttribute. ↩

  24. Unity Scripting API, HDROutputSettings.available. ↩

  25. Unity Scripting API, HDROutputSettings.active. ↩

  26. Unity Scripting API, HDROutputSettings.RequestHDRModeChange — Unity 6.0. The Japanese revision used the Unity 6.0 member page for exception conditions and request behavior, and checked the declaration against the Unity 6.3 class reference and C# reference source. It could not retrieve the 6.3 member page. Behavior in a Unity 6.3 runtime remains untested; this adaptation has not revalidated it. ↩

  27. Unity Scripting API, HDROutputSettings.HDRModeChangeRequested — Unity 6.0. The Japanese revision used the Unity 6.0 member page for exception conditions and checked the property declaration against the Unity 6.3 class reference and C# reference source. This is not a Unity 6.3 runtime test. ↩

  28. Unity Scripting API, HDROutputSettings. ↩

  29. Unity Technologies, UnityCsReference / 6000.3 / Graphics.bindings.cs. Used in the Japanese revision to check public property and method declarations in HDROutputSettings, not native implementation details or runtime behavior. ↩

  30. Unity Scripting API, HDROutputSettings.paperWhiteNits — Unity 6.3. ↩

  31. Unity Manual, Troubleshoot custom render passes with HDR output in URP. ↩

  32. Unity Scripting API, HDROutputSettings.automaticHDRTonemapping — Unity 6.3. ↩

  33. Unity Shader Graph 17.3 documentation, Saturate Node. ↩

  34. Unity Shader Graph 17.3 documentation, Color Node. ↩

  35. Unity Scripting API, GraphicsFormat. ↩

  36. Unity Manual, Implement a custom HDR overlay in URP. ↩

  37. Unity Scripting API, ImageConversion.EncodeToEXR. ↩

  38. Unity Manual, HDR Output debug views in URP. ↩

Top comments (0)