HDR, exposure and preserving highlights
Separate HDR, sRGB and exposure; save EXR without losing bright values.
HDR rendering keeps light values above 1 during intermediate calculations. This preserves differences between bright surfaces before exposure and tone mapping. It does not mean unlimited precision, an HDR monitor, or automatically realistic graphics.
Separate three operations
| Operation | Purpose | What it does not do |
|---|---|---|
| sRGB → linear | Decode color values before lighting calculations. sRGB 0.5 is approximately linear 0.214. | Compress bright highlights into a display range. |
| Exposure and tone mapping | Adjust scene brightness and map scene values to the intended output. | Restore details already clipped in a texture or image. |
| HDR display output | Deliver an appropriate signal to a supported display through a supported OS/backend configuration. | Turn an ordinary PNG or an 8-bit buffer into source HDR data. |
A linear → sRGB transfer alone is not tone mapping: an input of 4 still produces a value above 1. Neither step adds missing surface detail, correct normals, shadows or reflections.
Configure a 3D scene
- Add a
WorldEnvironmentEntity and assign anEnvironmentresource. Check whether the activeCamera3Dhas its own Environment override before editing a resource that may not affect the view. - Choose a tone mapper in the Environment's Tonemap section. Compare curves on the same camera and scene; a different curve changes highlight contrast and color, not just brightness.
- Set a stable exposure while adjusting lights. In code,
Environment.TonemapExposureis a multiplier, not an EV stop count. A multiplier of2doubles its input;0.5halves it. - If camera attributes are assigned, check their exposure multiplier and whether auto exposure is enabled. Disable automatic adaptation when comparing two lighting settings; otherwise the camera may compensate for the change.
- Check the frame at the intended renderer and output configuration. Renderer effects and HDR-display support are separate capabilities; do not assume the same result on every device.
See Environment and post-processing for the scene controls and HDR output for display output. There is no general “enable both sRGB switches” step in this workflow.
Keep color textures separate from data textures
Albedo/base-color textures normally contain encoded color. In a custom spatial shader, mark the corresponding color sampler with source_color so it is interpreted as color by the renderer. Normal maps, roughness, metallic and masks contain data: do not apply a color transfer just because they are image files. Use the appropriate material slot or shader hint for each map.
Avoid manually applying SrgbToLinear() to a value that has already been decoded by the material/shader path. Double conversion darkens the result. Conversely, a CPU Image.GetPixel() reads stored pixel values; it does not perform material sampling or lighting. A float Image format identifies storage precision, not the color space of its contents.
Save HDR image data without silently clipping it
Use a float format such as Image.Format.Rgbaf when creating CPU image data with values above 1. The following helper writes a one-pixel test image with RGB (4, 2, 0.25) to EXR. It is a file-data example, not a scene render or viewport screenshot.
using Reckfell;
public static class HdrData
{
// CPU image data, not a viewport capture or a display-ready screenshot.
public static Image CreateSample()
{
var image = Image.CreateEmpty(1, 1, false, Image.Format.Rgbaf);
image.SetPixel(0, 0, new Color(4f, 2f, 0.25f, 1f));
return image;
}
public static Error SaveSample(string path)
{
using var image = CreateSample();
return image.SaveExr(path, grayscale: false,
colorImage: false, maxLinearValue: -1f);
}
}Call HdrData.SaveSample("user://hdr-data.exr") and check the returned Error before using the file. Load it with Image.LoadFromFile() to inspect the values. On the tested Windows runtime, EXR retains (4, 2, 0.25); saving the same Image through SavePng() clips the first two channels to 1. PNG saving does not automatically apply the sRGB transfer needed to prepare arbitrary linear data for display.
The EXR options have specific meanings in 1.0.0:
colorImage: falsepreserves signed float data.trueclamps negative channel values to zero; it does not apply sRGB conversion or tone mapping.maxLinearValue: -1fdisables the upper clamp. A nonnegative value clamps stored channel values to that maximum; it is not an exposure adjustment.grayscale: truelabels a single-channel image as luminance. It does not turn an RGB image into grayscale.
The writer accepts uncompressed R/RG/RGB/RGBA 8-bit, half-float and float formats. Saving an 8-bit source as EXR cannot recover values or precision already lost. A build without the EXR writer returns Error.Unavailable; file and format failures also require checking the result. Verify codec availability in the actual exported build.
Capture a viewport deliberately
Wait for RenderingServer.FramePostDraw before reading Viewport.GetTexture().GetImage(); reading in _Ready() may return an old or blank frame. Then inspect the returned Image's format and the rendering path that produced it. A float format alone does not prove that this is the raw scene buffer before tone mapping.
Renaming a screenshot to .exr, converting an 8-bit Image to float, or calling SaveExr() after highlights were clipped does not recover the original lighting. The HDR 2D viewport option concerns its own linear HDR rendering path; it is not a universal raw-3D-buffer capture switch.
Diagnose the visible problem
| Symptom | Check first |
|---|---|
| White light sources or windows lose all detail | Compare with a lower fixed exposure; inspect source texture clipping and the tone curve. EXR saving will not repair an already clipped source. |
| Image looks too dark or washed out | Check color/data texture classification and duplicate or missing color conversion. |
| Brightness changes after moving the camera | Check auto exposure before changing every light. |
| Adjusting the Environment has no effect | Check the active camera, its Environment override and the WorldEnvironment used by that viewport. |
| Screenshot is black or stale | Wait for frame completion and verify that the viewport actually renders. |
| Light leaks through a wall or ceiling | Check geometry, normals, shadow casting and shadow settings. A tone mapper does not fix missing occlusion. |
The companion checks execute color transfers and CPU EXR/PNG roundtrips in the installed Windows editor. They do not validate GPU tone curves, physical exposure, HDR-monitor output, exported codecs or visual quality on other platforms.