Skip to main content

Tone Mapping & Exposure

Lighting calculations happen in high dynamic range: the intensity of a pixel can far exceed what a standard display can show. Tone mapping is the final transform that compresses this HDR range into displayable colors, and it has a major impact on the look of your scene. In PlayCanvas, tone mapping is set per camera.

Tone Mapping

The available tone mapping curves are:

  • TONEMAP_LINEAR — no compression; bright values simply clip at white. The default.
  • TONEMAP_FILMIC — a classic filmic curve with gentle roll-off in the highlights.
  • TONEMAP_HEJL — a punchy, contrasty filmic approximation.
  • TONEMAP_ACES — the Academy Color Encoding System curve, widely used for cinematic, photorealistic rendering.
  • TONEMAP_ACES2 — a variant of ACES with a different balance of contrast and saturation.
  • TONEMAP_NEUTRAL — compresses highlights while keeping hue and saturation shifts minimal; a good choice when accurate colors matter (e.g. product configurators).

For physically based scenes with HDR lighting, TONEMAP_ACES or TONEMAP_NEUTRAL are usually the best starting points.

camera.camera.toneMapping = pc.TONEMAP_ACES;
note

When post-processing is active via CameraFrame, tone mapping is applied by the post-processing pipeline instead — set it on the CameraFrame's rendering options rather than the camera.

Gamma Correction

After tone mapping, the camera's output is gamma-encoded for display on standard sRGB screens. This is controlled by gammaCorrection:

  • GAMMA_SRGB — output is encoded for sRGB displays. The default and recommended setting for all normal rendering.
  • GAMMA_NONE — output remains in linear space. This is only intended for advanced HDR pipelines where the output is rendered to an intermediate HDR texture that is tone mapped and gamma-corrected in a subsequent pass. On a standard display, it makes the scene appear too dark.

To understand why rendering is performed in linear space and gamma-encoded at the end, see Linear Workflow.

Physical Exposure

By default, scene brightness is controlled by a simple scene.exposure multiplier. Alternatively, the camera can model the exposure of a physical camera using three properties familiar to photographers:

app.scene.physicalUnits = true; // light intensities are now in physical units (lux)

camera.camera.aperture = 16; // f-stops — higher means less exposure (default: 16)
camera.camera.shutter = 1 / 1000; // seconds — longer means more exposure (default: 1/1000)
camera.camera.sensitivity = 1000; // ISO — higher means more exposure (default: 1000)
note

aperture, shutter and sensitivity only take effect when scene.physicalUnits is true. When it is false (the default), scene.exposure is used instead.

See it in action in the engine's physical units example:

Physical Light Units