Skip to content

Add ACES-based color management (MIXER COLORSPACE) to the OpenGL mixer#1758

Open
C-alcaide wants to merge 1 commit into
CasparCG:masterfrom
C-alcaide:feature/ogl-aces-colorspace
Open

Add ACES-based color management (MIXER COLORSPACE) to the OpenGL mixer#1758
C-alcaide wants to merge 1 commit into
CasparCG:masterfrom
C-alcaide:feature/ogl-aces-colorspace

Conversation

@C-alcaide

@C-alcaide C-alcaide commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Disclamer: AI powered PR. Since we're just applying industry standards and color science it should be pretty straightforward. This will be helpful in situations where the files don't have color space metdata or it's wrong (unreal engine exporting prores files does this, for example). This is also vital for HDR pipelines, since now we have full control of the files EOTFs and OETFs. I started this work some months ago and I think it's now refined enough to join the official project if you deem it appropiate.

There are equivalent vulkan mixer shaders that I could merge as well, but I think it's better to focus on the current state of the server for now.

Summary

This PR adds a per-layer ACES-based color management stage to the OpenGL image mixer, exposed through a new MIXER COLORSPACE AMCP command. It lets an operator convert a layer between transfer functions (EOTFs/OETFs) and color gamuts, apply an exposure adjustment, and optionally run a tone-mapping operator — all in linear light on the GPU.

This is the first in a planned series of small PRs upstreaming color-grading features. It is intentionally self-contained: no channel configuration, no automatic HDR/SDR conversion, no new render-graph plumbing. When MIXER COLORSPACE is not used, the shader path is unchanged (the grading block is gated behind a color_grading uniform that defaults to off).

What it does

For each layer with color management enabled, the fragment shader runs this pipeline in order:

  1. Input EOTF — decode the source signal to linear (sRGB, Rec.709/BT.1886, PQ, HLG, ARRI LogC3, Sony S-Log3, or linear).
  2. Luminance adaptation — BT.2408-based scaling when crossing HDR/SDR domains.
  3. Input gamut → working space — 3×3 matrix into ACEScg (AP1). Direct D65↔D65 matrices are used for BT.709↔BT.2020 when no tone mapping is requested, to avoid chromatic-adaptation artifacts from the ACEScg (D60) intermediate.
  4. Exposure — linear multiplier.
  5. Tone mapping (optional) — Reinhard, ACES filmic approximation, or full ACES RRT/ODT variants (RRT, RRT+709, RRT+P3, RRT+2020 PQ).
  6. Working space → output gamut — 3×3 matrix.
  7. Clamp — only when no tone mapping is applied.
  8. Output OETF — encode to the target signal.

AMCP usage

MIXER [video_channel]{-[layer]} COLORSPACE {[input_transfer] [input_gamut] [tonemapping] [output_gamut] [output_transfer] [exposure]}
MIXER [video_channel]{-[layer]} COLORSPACE NONE
  • input_transfer / output_transfer: LINEAR, SRGB, REC709, PQ, HLG, LOGC3, SLOG3
  • input_gamut / output_gamut: BT709, BT2020, DCIP3, ACES_AP0, ACES_AP1 (alias ACESCG), ARRI_WG3, SGAMUT3_CINE
  • tonemapping: NONE, REINHARD, ACES_FILMIC, ACES_RRT, ACES_RRT_709, ACES_RRT_P3, ACES_RRT_2020_PQ
  • exposure: linear multiplier (default 1.0)
  • output_transfer defaults to SRGB when omitted; gamuts/tonemapping default to their first value.

Calling COLORSPACE with no arguments queries the current state. COLORSPACE NONE disables grading for the layer.

Examples

# Grade ARRI LogC3 / AWG3 footage through the ACES RRT to a Rec.709 deliverable
MIXER 1-10 COLORSPACE LOGC3 ARRI_WG3 ACES_RRT BT709 REC709

# Tone-map a BT.2020 PQ HDR source down to sRGB with Reinhard
MIXER 1-10 COLORSPACE PQ BT2020 REINHARD BT709 SRGB

# Disable color management on the layer
MIXER 1-10 COLORSPACE NONE

Implementation notes

  • core::color_grade struct added to image_transform (transform-tweened exposure; equality + tween wiring in frame_transform.cpp).
  • Transform combination handled in accelerator/ogl/util/transforms.cpp (a child layer's grade overrides the inherited one when enabled).
  • Shader work is isolated to shader.frag: EOTF/OETF/tone-map helpers plus one contiguous color-management block in main(), all gated behind color_grading.
  • image_kernel.cpp sets the uniforms and the gamut matrices. Display peak luminance for the HLG OOTF path is currently fixed at 1000 nits.
  • New AMCP command MIXER COLORSPACE in AMCPCommandsImpl.cpp.

Testing

  • Builds cleanly (Release, MSVC) — casparcg.exe links with no warnings from the changed files.
  • Verified the shader path is unchanged when color_grading is off.
  • Smoke-tested at runtime with the example commands above.

Full operator documentation will be added to the wiki.

Adds a per-layer ACES-based color management stage to the OpenGL image
mixer, exposed via a new MIXER COLORSPACE AMCP command. Converts a layer
between transfer functions (EOTFs/OETFs) and color gamuts, applies an
exposure adjustment, and optionally runs a tone-mapping operator, all in
linear light on the GPU.

The grading path is gated behind a color_grading shader uniform that
defaults to off, so the existing shader output is unchanged when the
command is not used.

- core::color_grade struct added to image_transform (exposure tween +
  equality wiring in frame_transform.cpp)
- transform combination handled in accelerator/ogl/util/transforms.cpp
- shader.frag: EOTF/OETF/tone-map helpers + gated color-management block
- image_kernel.cpp: gamut matrices and uniform setup
- AMCPCommandsImpl.cpp: MIXER COLORSPACE command, parsers, registration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant