ACES 2.0 CAM DRT Development

Hi Nick,
I won’t post another plot, but the lighter “ring” inside the blue star gets more prominent with the second fix. With the first fix it was less.

Is that centred on 288 or 250? The original node version was 250, and then v2 was 288 until I just changed it.

It’s getting confusing for me :slight_smile:
Your first non Blink gizmo looks identical to the latest version with blink for me (250).

The one I tried in the meantime was the one with 288 and this made the “ring” more visible again.

Sorry for not being clear. The original (built from multiple nodes) version was centred on 250. When I built the second (v2) pure Blink version I originally set it to default to 288 (although it is obviously a user parameter). Yesterday when I pushed the DCTL version I set that to default to 250, matching* the original version, but I also changed v2 to default to 250 as well.

* When I say “matching” they won’t match exactly, because although the original version was centred on 250, it uses spline curves in a Nuke ColorLookup node, where the pure Blink and DCTL versions use a “bump function” with similar but not identical shape.

1 Like

I’ve tested Nick Shaw’s DCTL version of v55:

  • The noise is more noticeable compared to v52

  • Some colours still appear too bright in SDR (for example yellow and magenta in ACES OT VWG Sample Frame - 0029). I had “Encode as BT.2100 PQ” checked to compare SDR and HDR on the same monitor.

My theory is the following:

The compression of lightness J is much stronger in SDR than in HDR. Maybe some colours that tend to be bright (yellow, orange, magenta, cyan) need some additional compression of lightness J.

There is also the possibility that my monitor is just deceiving me.

Looking again at the chromaticity plot from @TooDee’s star image , it seems to me that the sharpness of the “double back” in the plot is a result of two things.

Firstly, and probably most importantly, it is not actually as sharp as it looks, because we are viewing a 2D projection of a line which in 3D space actually curves down and back in.

Secondly, the corner is sharpened by the fact that in the blue corner, the blue primary of the limiting Rec.709 gamut and the reach AP1 gamut are very close to each other. The compression curve uses threshold and limit parameters calculated as:

limit = \frac{M_{reach}}{M_{boundary}}
threshold = \frac{1}{limit}

This means that when the reach and boundary M values are close, as they are in the blue corner, then limit is only just above 1.0 and threshold is only just below it, resulting in quite a sharp transition into compression.

I remember @priikone added this varying threshold to address a particular issue, but I cannot remember what that issue was. In any case, because other things have changed since then, it may be worth revisiting it to see if the issue it solved still exists, and whether what its negative impact in the blue corner is a good tradeoff against its benefits elsewhere.


The issue was that if the limt is close to 1.0 and the threshold is constant (like 0.75), it wouldn’t compress much at all. It was causing artifacting in HDR as it was just clipping rather than compressing. So even a small amount of compression fixed the artifacts (I think we tried to look at some of those artifacts in one of the meetings also).

Ah yes. I remember now.

Presumably the artefacts are caused by the imprecision of the gamut approximation, followed by final clipping. Because in theory, if things were precise “not compressing much” would be the right thing to do, as only a very small amount of compression would be needed to compress the AP1 boundary to the limiting gamut boundary, because they are already close.

Or maybe moving the threshold is about affecting the slope of the compression curve as it hits 1.0.

I just opened a new pull request #42 for @alexfry for CAM DRT v057, also available from my repo.

This version fixes the white point used in the computation of various tables, especially the reach table and the AP1 gamut cusp table. The white point for them is now the ACES white rather than D65.

As a result the inverse improved, so this version now reduces the cusp smoothing value and smoothing factors without negatively impacting the inverse. The end result is slightly less clipping in the forward direction. There’s not really a visible difference to v056.

@TooDee’s stars chromaticities v057 vs v056 in sRGB output:



BT.1886 inverse v057 vs v056:



1 Like

Following @priikone’s suggestion in last night’s meeting, I’ve added optional Björn Compress Mode to the Nuke and DCTL implementations of my LMT.

They currently still use the LMS matrix from v55, but since with Björn compression they will be using a JMh space which doesn’t match the one in the DRT anyway, the exact LMS matrix didn’t seem important.

The compress mode certainly seems to reduce the darkening of the inner area of the blue star.

I also noticed that we could also reduce the lower hull gamma from 1.14 to 1.139 which reduces clipping even more, without impacting the inverse. So maybe in next version.

After yesterday’s meeting I went over hellwig model again , and I was able to simplify the color model itself it to just a few nodes, I am getting the same values as v55 in DCTL

I wanted to do this to understand the behaviors. If you see the nodes the color model can be explain the following way:

  1. convert to XYZ

  2. convert to the LMS space which is technically just an RGB space with a red primary slightly outside XYZ, a green primary so far outside that it breaks the xyY calculation, and a blue primary slightly outside XYZ.

  3. re-normalize achromatic using gains

  4. RGB values are transformed using a sigmoid function.
    The original model likely only dealt with values between 0 and 1 so not a huge problem for that case. We can achieve a similar effect with a simple power function and plus some gain, although this gain gets canceled later on in step 7.
    This step, like any adjustments made to individual color channels,this is what cause hue shifts. It breakes values outside the RGB range, and while DRT helps a bit, it doesn’t fully address the issue.
    Similar to other per-channel adjustments, this step pushes colors towards the primaries of the LMS/RGB color space its applied in. When compared to common display standards (P3, Rec.709, Rec.2020), the color space used here has more magenta in the blue and red channels, and more cyan in the green channel. This significantly shifts colors towards magenta and cyan which is the opposite of aesthetically pleasing results, it not as noticeable due to the rest of the DRT not been per-channel.
    Another drawback of this step, like with other per-channel adjustments, is a slight tendency to push colors towards achromatic.
    Screenshot 2024-03-28 122740

  5. Rotate the cube to be up-right, plus scale the 3 channels to be better spread around.

  6. convert to a cylindrical coordinates space.

  7. scale achromatic to make J channel 1 = Luminace 1, for achromatic, the rest still scaled differently.

hellwigModel.nk (35.7 KB)


Just pushed a set of v57 DCTLs to my repo

That is an excellent analysis @jpzambrano9 Well done ! I am pretty sure many people on this forum will appreciate this deconstruction of the model.

I could not help but think that per-channel was behind this behavior when I heard Pekka´s question in yesterday´s meeting (around 30:00).

Why is it that with this transform the skew is always towards the secondaries ?

If I recall correctly, the first person who explained to me this behavior was Troy and even if I did not understand all the maths behind it, I put it in my article since it seemed relevant.

Just for fun, I will share here a meme:

Hope you don´t mind the cheeky Scooby doo twist :wink:

Happy Easter everyone !


That’s really nice and I can confirm that it produces correct values (after a little bit of tweaking). Only difference I notice is with extreme input values that in the DRT would need the AP1 clamp. Those don’t come out the same. In the DRT (without the AP1 clamp) the M collapses to 0 but in your node the M is a positive value (which I consider better behavior). The J however collapses to 0 in both of them.

Pushed an update correcting the cusp smoothing parameters for v57. Thanks @priikone for noticing my mistake.

Nick, to be clear you have only this latest update to hellwig_lib_v057.h ?

This should be Hellwig\ RGB_{aw/wc} rather, the sigmoid compression is applied to LMS cone space values, not to the JMh correlates. Also worth noting that the model was fitted whilst accounting for the clumping. The model is designed to predict the appearance of patch of colours under different viewing conditions, it was not built for colour rendering and it should be expected that changing things inside for colour rendering might introduce/uncover undesirable behaviours.

Nick, found error in latest ver057 hellwig_lib_057. It works on my iPad Pro but not on my AMD Windows system. Following is error log:

[0x00001580] | DVIP | ERROR | 2024-03-30 07:17:55,257 | DaVinci CTL compilation failed.

[0x00001580] | DVIP | ERROR | 2024-03-30 07:17:55,257 | Failed to build program.
Build Status: -2
Build Options: -w -DDEVICE_IS_OPENCL -DCOMPILER_IS_DVIP_RT -DKERNEL_TYPE_IS_DCTL -DVENDOR_IS_AMD -cl-mad-enable -cl-fast-relaxed-math
Build Log: In file included from T:\Users\JDM\AppData\Local\Temp\
C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\LUT\DCTL\ACES2\ver057\hellwig_lib_v057.h:541:12: error: expected expression
return int((high + low) / 2); // Integer division
C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\LUT\DCTL\ACES2\ver057\hellwig_lib_v057.h:557:12: error: expected expression
return int(wrapped_hue / 360.0f * table_size);
2 errors generated.

adding an extra line to each fixes it, for example:

int iback = wrapped_hue / 360.0f * table_size;
return iback;
1 Like