Skip to content

fix: apply Color LUT in correct sRGB color space#8747

Merged
mvaligursky merged 1 commit into
mainfrom
mv-color-lut-srgb-fix
May 18, 2026
Merged

fix: apply Color LUT in correct sRGB color space#8747
mvaligursky merged 1 commit into
mainfrom
mv-color-lut-srgb-fix

Conversation

@mvaligursky

Copy link
Copy Markdown
Contributor

Fixes incorrect color output when CameraFrame.colorLUT is used with non-trivial LUTs.

LUTs authored in tools like Photoshop and Unreal are encoded in sRGB display space — both their lookup coordinates and stored values are sRGB. The compose pass was indexing the LUT with linear values and treating the sampled sRGB value as linear, producing visibly desaturated and dim output for aggressive LUTs (#8527). With gentle LUTs the mismatch was mostly invisible; with strong tints it was significant.

Changes:

  • Compose shader (GLSL and WGSL) now encodes the linear input to sRGB for the LUT lookup coordinate.
  • LUT texture is expected to be loaded in an sRGB pixel format. The GPU sampler decodes the sRGB-encoded LUT values to linear, so they can be blended with the linear scene directly.
  • Cost: one pow per pixel in the LUT path.
  • RenderPassCompose.set colorLUT runs a debug-build validation (wrapped in Debug.call, stripped in release) that checks the texture's srgb, mipmaps, minFilter and magFilter and emits a single Debug.warnOnce listing exactly which settings the LUT needs.

API note:

LUT textures must now be loaded with srgb: true, mipmaps: false, and FILTER_LINEAR for both min and mag filters. Existing code that loaded LUTs with default options will get a one-time debug warning naming exactly what to change, e.g.:

CameraFrame.colorLUT: texture 'colorLut' should be configured with: srgb: true; mipmaps: false; minFilter: FILTER_LINEAR.

Before:

new pc.Asset('lut', 'texture', { url: '...' });

After:

new pc.Asset(
    'lut',
    'texture',
    { url: '...' },
    {
        srgb: true,
        mipmaps: false,
        minfilter: 'linear'
    }
);

In the PlayCanvas Editor, tick sRGB, untick Mipmaps, and set the filter dropdown to Linear on the LUT texture asset.

Examples:

  • Updated graphics/hdr example to load its lut-blue asset with the required settings.

Assets:

  • Added examples/assets/cube-luts/lut-neutral.png (identity LUT) and lut-cherry.png (aggressive red LUT) for use in a follow-up dedicated LUT example.

Fixed #8527

LUTs authored in tools like Photoshop and Unreal are encoded in sRGB display
space — both their lookup coordinates and stored values are sRGB. The compose
pass was indexing the LUT with linear values and treating the sampled sRGB
value as linear, producing visibly desaturated and dim output for aggressive
LUTs. With gentle LUTs the mismatch was mostly invisible.

The compose shader (GLSL and WGSL) now encodes the linear input to sRGB for
the LUT lookup coordinate. The LUT texture is expected to be loaded in an
sRGB pixel format so the hardware sampler decodes the LUT values to linear
on sample, which is then blended with the linear scene directly.

RenderPassCompose.set colorLUT runs a debug-build validation that checks
the texture srgb, mipmaps, minFilter and magFilter and emits a single
Debug.warnOnce listing the settings the LUT needs.

Fixed #8527
@mvaligursky mvaligursky self-assigned this May 18, 2026
@mvaligursky mvaligursky added the area: graphics Graphics related issue label May 18, 2026
@mvaligursky mvaligursky merged commit d9e2b8c into main May 18, 2026
8 checks passed
@mvaligursky mvaligursky deleted the mv-color-lut-srgb-fix branch May 18, 2026 17:22
mvaligursky added a commit to playcanvas/developer-site that referenced this pull request May 18, 2026
…964)

Adds a Color LUT subsection to the HDR Rendering guide covering how to load
and use CameraFrame.colorLUT with the texture-format contract introduced in
playcanvas/engine#8747 (sRGB on, mipmaps off, linear filtering).

Includes the Unreal-format layout description, required texture settings
with rationale, a full asset-loading + assignment code snippet, the
authoring workflow, and embedded neutral and cherry example LUTs as visual
references. The CameraFrame index page LUT bullet links to the new section.

Japanese mirror updated to match.

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: graphics Graphics related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CameraFrame.ColorLut produces incorrect results

1 participant