From a10eea6e4ce647061813519d5b0ea496f29495b9 Mon Sep 17 00:00:00 2001
From: leonard Wu <364452445@qq.com>
Date: 星期四, 09 八月 2018 09:47:08 +0800
Subject: [PATCH] 同步最新svn内容

---
 Assets/Plugins/PostProcessing/Resources/Shaders/ACES.cginc | 2666 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 1,333 insertions(+), 1,333 deletions(-)

diff --git a/Assets/Plugins/PostProcessing/Resources/Shaders/ACES.cginc b/Assets/Plugins/PostProcessing/Resources/Shaders/ACES.cginc
index fa996b9..7be42b6 100644
--- a/Assets/Plugins/PostProcessing/Resources/Shaders/ACES.cginc
+++ b/Assets/Plugins/PostProcessing/Resources/Shaders/ACES.cginc
@@ -1,1333 +1,1333 @@
-#ifndef __ACES__
-#define __ACES__
-
-/**
- * https://github.com/ampas/aces-dev
- *
- * Academy Color Encoding System (ACES) software and tools are provided by the
- * Academy under the following terms and conditions: A worldwide, royalty-free,
- * non-exclusive right to copy, modify, create derivatives, and use, in source and
- * binary forms, is hereby granted, subject to acceptance of this license.
- *
- * Copyright 2015 Academy of Motion Picture Arts and Sciences (A.M.P.A.S.).
- * Portions contributed by others as indicated. All rights reserved.
- *
- * Performance of any of the aforementioned acts indicates acceptance to be bound
- * by the following terms and conditions:
- *
- * * Copies of source code, in whole or in part, must retain the above copyright
- * notice, this list of conditions and the Disclaimer of Warranty.
- *
- * * Use in binary form must retain the above copyright notice, this list of
- * conditions and the Disclaimer of Warranty in the documentation and/or other
- * materials provided with the distribution.
- *
- * * Nothing in this license shall be deemed to grant any rights to trademarks,
- * copyrights, patents, trade secrets or any other intellectual property of
- * A.M.P.A.S. or any contributors, except as expressly stated herein.
- *
- * * Neither the name "A.M.P.A.S." nor the name of any other contributors to this
- * software may be used to endorse or promote products derivative of or based on
- * this software without express prior written permission of A.M.P.A.S. or the
- * contributors, as appropriate.
- *
- * This license shall be construed pursuant to the laws of the State of
- * California, and any disputes related thereto shall be subject to the
- * jurisdiction of the courts therein.
- *
- * Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
- * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL A.M.P.A.S., OR ANY
- * CONTRIBUTORS OR DISTRIBUTORS, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, RESITUTIONARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE ACADEMY SPECIFICALLY
- * DISCLAIMS ANY REPRESENTATIONS OR WARRANTIES WHATSOEVER RELATED TO PATENT OR
- * OTHER INTELLECTUAL PROPERTY RIGHTS IN THE ACADEMY COLOR ENCODING SYSTEM, OR
- * APPLICATIONS THEREOF, HELD BY PARTIES OTHER THAN A.M.P.A.S.,WHETHER DISCLOSED OR
- * UNDISCLOSED.
- */
-
-//#define CUSTOM_WHITE_POINT
-
-/*
-    Basic usage :
-
-    half4 color = tex2D(_MainTex, i.uv);
-    half3 aces = unity_to_ACES(color.rgb);
-    half3 oces = RRT(aces);
-    half3 odt = ODT_RGBmonitor_100nits_dim(oces);
-    return half4(odt, color.a);
-
-    If you want to customize the white point, uncomment the previous define and set uniforms accordingly:
-
-    float whitePoint = 48f; // Default ACES value
-    material.SetFloat("CINEMA_WHITE", whitePoint);
-    material.SetFloat("CINEMA_DARK", whitePoint / 2400f);
- */
-
-#include "Common.cginc"
-
-#define ACEScc_MAX      1.4679964
-#define ACEScc_MIDGRAY  0.4135884
-
-//
-// Precomputed matrices (pre-transposed)
-// See https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
-//
-static const half3x3 sRGB_2_AP0 = {
-    0.4397010, 0.3829780, 0.1773350,
-    0.0897923, 0.8134230, 0.0967616,
-    0.0175440, 0.1115440, 0.8707040
-};
-
-static const half3x3 sRGB_2_AP1 = {
-    0.61319, 0.33951, 0.04737,
-    0.07021, 0.91634, 0.01345,
-    0.02062, 0.10957, 0.86961
-};
-
-static const half3x3 AP0_2_sRGB = {
-    2.52169, -1.13413, -0.38756,
-    -0.27648, 1.37272, -0.09624,
-    -0.01538, -0.15298, 1.16835,
-};
-
-static const half3x3 AP1_2_sRGB = {
-    1.70505, -0.62179, -0.08326,
-    -0.13026, 1.14080, -0.01055,
-    -0.02400, -0.12897, 1.15297,
-};
-
-static const half3x3 AP0_2_AP1_MAT = {
-     1.4514393161, -0.2365107469, -0.2149285693,
-    -0.0765537734,  1.1762296998, -0.0996759264,
-     0.0083161484, -0.0060324498,  0.9977163014
-};
-
-static const half3x3 AP1_2_AP0_MAT = {
-     0.6954522414, 0.1406786965, 0.1638690622,
-     0.0447945634, 0.8596711185, 0.0955343182,
-    -0.0055258826, 0.0040252103, 1.0015006723
-};
-
-static const half3x3 AP1_2_XYZ_MAT = {
-     0.6624541811, 0.1340042065, 0.1561876870,
-     0.2722287168, 0.6740817658, 0.0536895174,
-    -0.0055746495, 0.0040607335, 1.0103391003
-};
-
-static const half3x3 XYZ_2_AP1_MAT = {
-     1.6410233797, -0.3248032942, -0.2364246952,
-    -0.6636628587,  1.6153315917,  0.0167563477,
-     0.0117218943, -0.0082844420,  0.9883948585
-};
-
-static const half3x3 XYZ_2_REC709_MAT = {
-     3.2409699419, -1.5373831776, -0.4986107603,
-    -0.9692436363,  1.8759675015,  0.0415550574,
-     0.0556300797, -0.2039769589,  1.0569715142
-};
-
-static const half3x3 XYZ_2_REC2020_MAT = {
-     1.7166511880, -0.3556707838, -0.2533662814,
-    -0.6666843518,  1.6164812366,  0.0157685458,
-     0.0176398574, -0.0427706133,  0.9421031212
-};
-
-static const half3x3 XYZ_2_DCIP3_MAT = {
-     2.7253940305, -1.0180030062, -0.4401631952,
-    -0.7951680258,  1.6897320548,  0.0226471906,
-     0.0412418914, -0.0876390192,  1.1009293786
-};
-
-static const half3 AP1_RGB2Y = half3(0.272229, 0.674082, 0.0536895);
-
-static const half3x3 RRT_SAT_MAT = {
-    0.9708890, 0.0269633, 0.00214758,
-    0.0108892, 0.9869630, 0.00214758,
-    0.0108892, 0.0269633, 0.96214800
-};
-
-static const half3x3 ODT_SAT_MAT = {
-    0.949056, 0.0471857, 0.00375827,
-    0.019056, 0.9771860, 0.00375827,
-    0.019056, 0.0471857, 0.93375800
-};
-
-static const half3x3 D60_2_D65_CAT = {
-     0.98722400, -0.00611327, 0.0159533,
-    -0.00759836,  1.00186000, 0.0053302,
-     0.00307257, -0.00509595, 1.0816800
-};
-
-//
-// Unity to ACES
-//
-// converts Unity raw (sRGB primaries) to
-//          ACES2065-1 (AP0 w/ linear encoding)
-//
-half3 unity_to_ACES(half3 x)
-{
-    x = mul(sRGB_2_AP0, x);
-    return x;
-}
-
-//
-// ACES to Unity
-//
-// converts ACES2065-1 (AP0 w/ linear encoding)
-//          Unity raw (sRGB primaries) to
-//
-half3 ACES_to_unity(half3 x)
-{
-    x = mul(AP0_2_sRGB, x);
-    return x;
-}
-
-//
-// Unity to ACEScg
-//
-// converts Unity raw (sRGB primaries) to
-//          ACEScg (AP1 w/ linear encoding)
-//
-half3 unity_to_ACEScg(half3 x)
-{
-    x = mul(sRGB_2_AP1, x);
-    return x;
-}
-
-//
-// ACEScg to Unity
-//
-// converts ACEScg (AP1 w/ linear encoding) to
-//          Unity raw (sRGB primaries)
-//
-half3 ACEScg_to_unity(half3 x)
-{
-    x = mul(AP1_2_sRGB, x);
-    return x;
-}
-
-//
-// ACES Color Space Conversion - ACES to ACEScc
-//
-// converts ACES2065-1 (AP0 w/ linear encoding) to
-//          ACEScc (AP1 w/ logarithmic encoding)
-//
-// This transform follows the formulas from section 4.4 in S-2014-003
-//
-half ACES_to_ACEScc(half x)
-{
-    if (x <= 0.0)
-        return -0.35828683; // = (log2(pow(2.0, -15.0) * 0.5) + 9.72) / 17.52
-    else if (x < pow(2.0, -15.0))
-        return (log2(pow(2.0, -16.0) + x * 0.5) + 9.72) / 17.52;
-    else // (x >= pow(2.0, -15.0))
-        return (log2(x) + 9.72) / 17.52;
-}
-
-half3 ACES_to_ACEScc(half3 x)
-{
-    x = clamp(x, 0.0, HALF_MAX);
-
-    // x is clamped to [0, HALF_MAX], skip the <= 0 check
-    return (x < 0.00003051757) ? (log2(0.00001525878 + x * 0.5) + 9.72) / 17.52 : (log2(x) + 9.72) / 17.52;
-
-    /*
-    return half3(
-        ACES_to_ACEScc(x.r),
-        ACES_to_ACEScc(x.g),
-        ACES_to_ACEScc(x.b)
-    );
-    */
-}
-
-//
-// ACES Color Space Conversion - ACEScc to ACES
-//
-// converts ACEScc (AP1 w/ ACESlog encoding) to
-//          ACES2065-1 (AP0 w/ linear encoding)
-//
-// This transform follows the formulas from section 4.4 in S-2014-003
-//
-half ACEScc_to_ACES(half x)
-{
-    // TODO: Optimize me
-    if (x < -0.3013698630) // (9.72 - 15) / 17.52
-        return (pow(2.0, x * 17.52 - 9.72) - pow(2.0, -16.0)) * 2.0;
-    else if (x < (log2(HALF_MAX) + 9.72) / 17.52)
-        return pow(2.0, x * 17.52 - 9.72);
-    else // (x >= (log2(HALF_MAX) + 9.72) / 17.52)
-        return HALF_MAX;
-}
-
-half3 ACEScc_to_ACES(half3 x)
-{
-    return half3(
-        ACEScc_to_ACES(x.r),
-        ACEScc_to_ACES(x.g),
-        ACEScc_to_ACES(x.b)
-    );
-}
-
-//
-// ACES Color Space Conversion - ACES to ACEScg
-//
-// converts ACES2065-1 (AP0 w/ linear encoding) to
-//          ACEScg (AP1 w/ linear encoding)
-//
-half3 ACES_to_ACEScg(half3 x)
-{
-    return mul(AP0_2_AP1_MAT, x);
-}
-
-//
-// ACES Color Space Conversion - ACEScg to ACES
-//
-// converts ACEScg (AP1 w/ linear encoding) to
-//          ACES2065-1 (AP0 w/ linear encoding)
-//
-half3 ACEScg_to_ACES(half3 x)
-{
-    return mul(AP1_2_AP0_MAT, x);
-}
-
-//
-// Reference Rendering Transform (RRT)
-//
-//   Input is ACES
-//   Output is OCES
-//
-half rgb_2_saturation(half3 rgb)
-{
-    const half TINY = 1e-10;
-    half mi = Min3(rgb);
-    half ma = Max3(rgb);
-    return (max(ma, TINY) - max(mi, TINY)) / max(ma, 1e-2);
-}
-
-half rgb_2_yc(half3 rgb)
-{
-    const half ycRadiusWeight = 1.75;
-
-    // Converts RGB to a luminance proxy, here called YC
-    // YC is ~ Y + K * Chroma
-    // Constant YC is a cone-shaped surface in RGB space, with the tip on the
-    // neutral axis, towards white.
-    // YC is normalized: RGB 1 1 1 maps to YC = 1
-    //
-    // ycRadiusWeight defaults to 1.75, although can be overridden in function
-    // call to rgb_2_yc
-    // ycRadiusWeight = 1 -> YC for pure cyan, magenta, yellow == YC for neutral
-    // of same value
-    // ycRadiusWeight = 2 -> YC for pure red, green, blue  == YC for  neutral of
-    // same value.
-
-    half r = rgb.x;
-    half g = rgb.y;
-    half b = rgb.z;
-    half chroma = sqrt(b * (b - g) + g * (g - r) + r * (r - b));
-    return (b + g + r + ycRadiusWeight * chroma) / 3.0;
-}
-
-half rgb_2_hue(half3 rgb)
-{
-    // Returns a geometric hue angle in degrees (0-360) based on RGB values.
-    // For neutral colors, hue is undefined and the function will return a quiet NaN value.
-    half hue;
-    if (rgb.x == rgb.y && rgb.y == rgb.z)
-        hue = 0.0; // RGB triplets where RGB are equal have an undefined hue
-    else
-        hue = (180.0 / UNITY_PI) * atan2(sqrt(3.0) * (rgb.y - rgb.z), 2.0 * rgb.x - rgb.y - rgb.z);
-
-    if (hue < 0.0) hue = hue + 360.0;
-
-    return hue;
-}
-
-half center_hue(half hue, half centerH)
-{
-    half hueCentered = hue - centerH;
-    if (hueCentered < -180.0) hueCentered = hueCentered + 360.0;
-    else if (hueCentered > 180.0) hueCentered = hueCentered - 360.0;
-    return hueCentered;
-}
-
-half sigmoid_shaper(half x)
-{
-    // Sigmoid function in the range 0 to 1 spanning -2 to +2.
-
-    half t = max(1.0 - abs(x / 2.0), 0.0);
-    half y = 1.0 + sign(x) * (1.0 - t * t);
-
-    return y / 2.0;
-}
-
-half glow_fwd(half ycIn, half glowGainIn, half glowMid)
-{
-    half glowGainOut;
-
-    if (ycIn <= 2.0 / 3.0 * glowMid)
-        glowGainOut = glowGainIn;
-    else if (ycIn >= 2.0 * glowMid)
-        glowGainOut = 0.0;
-    else
-        glowGainOut = glowGainIn * (glowMid / ycIn - 1.0 / 2.0);
-
-    return glowGainOut;
-}
-
-/*
-half cubic_basis_shaper
-(
-    half x,
-    half w   // full base width of the shaper function (in degrees)
-)
-{
-    half M[4][4] = {
-        { -1.0 / 6,  3.0 / 6, -3.0 / 6,  1.0 / 6 },
-        {  3.0 / 6, -6.0 / 6,  3.0 / 6,  0.0 / 6 },
-        { -3.0 / 6,  0.0 / 6,  3.0 / 6,  0.0 / 6 },
-        {  1.0 / 6,  4.0 / 6,  1.0 / 6,  0.0 / 6 }
-    };
-
-    half knots[5] = {
-        -w / 2.0,
-        -w / 4.0,
-             0.0,
-         w / 4.0,
-         w / 2.0
-    };
-
-    half y = 0.0;
-    if ((x > knots[0]) && (x < knots[4]))
-    {
-        half knot_coord = (x - knots[0]) * 4.0 / w;
-        int j = knot_coord;
-        half t = knot_coord - j;
-
-        half monomials[4] = { t*t*t, t*t, t, 1.0 };
-
-        // (if/else structure required for compatibility with CTL < v1.5.)
-        if (j == 3)
-        {
-            y = monomials[0] * M[0][0] + monomials[1] * M[1][0] +
-                monomials[2] * M[2][0] + monomials[3] * M[3][0];
-        }
-        else if (j == 2)
-        {
-            y = monomials[0] * M[0][1] + monomials[1] * M[1][1] +
-                monomials[2] * M[2][1] + monomials[3] * M[3][1];
-        }
-        else if (j == 1)
-        {
-            y = monomials[0] * M[0][2] + monomials[1] * M[1][2] +
-                monomials[2] * M[2][2] + monomials[3] * M[3][2];
-        }
-        else if (j == 0)
-        {
-            y = monomials[0] * M[0][3] + monomials[1] * M[1][3] +
-                monomials[2] * M[2][3] + monomials[3] * M[3][3];
-        }
-        else
-        {
-            y = 0.0;
-        }
-    }
-
-    return y * 3.0 / 2.0;
-}
-*/
-
-static const half3x3 M = {
-     0.5, -1.0, 0.5,
-    -1.0,  1.0, 0.0,
-     0.5,  0.5, 0.0
-};
-
-half segmented_spline_c5_fwd(half x)
-{
-    const half coefsLow[6] = { -4.0000000000, -4.0000000000, -3.1573765773, -0.4852499958, 1.8477324706, 1.8477324706 }; // coefs for B-spline between minPoint and midPoint (units of log luminance)
-    const half coefsHigh[6] = { -0.7185482425, 2.0810307172, 3.6681241237, 4.0000000000, 4.0000000000, 4.0000000000 }; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
-    const half2 minPoint = half2(0.18 * exp2(-15.0), 0.0001); // {luminance, luminance} linear extension below this
-    const half2 midPoint = half2(0.18, 0.48); // {luminance, luminance}
-    const half2 maxPoint = half2(0.18 * exp2(18.0), 10000.0); // {luminance, luminance} linear extension above this
-    const half slopeLow = 0.0; // log-log slope of low linear extension
-    const half slopeHigh = 0.0; // log-log slope of high linear extension
-
-    const int N_KNOTS_LOW = 4;
-    const int N_KNOTS_HIGH = 4;
-
-    // Check for negatives or zero before taking the log. If negative or zero,
-    // set to ACESMIN.1
-    float xCheck = x;
-    if (xCheck <= 0.0) xCheck = 0.00006103515; // = pow(2.0, -14.0);
-
-    half logx = log10(xCheck);
-    half logy;
-
-    if (logx <= log10(minPoint.x))
-    {
-        logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
-    }
-    else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
-    {
-        half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
-        int j = knot_coord;
-        half t = knot_coord - j;
-
-        half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
-        half3 monomials = half3(t * t, t, 1.0);
-        logy = dot(monomials, mul(M, cf));
-    }
-    else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
-    {
-        half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
-        int j = knot_coord;
-        half t = knot_coord - j;
-
-        half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
-        half3 monomials = half3(t * t, t, 1.0);
-        logy = dot(monomials, mul(M, cf));
-    }
-    else
-    { //if (logIn >= log10(maxPoint.x)) {
-        logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
-    }
-
-    return pow(10.0, logy);
-}
-
-half segmented_spline_c9_fwd(half x)
-{
-    const half coefsLow[10] = { -1.6989700043, -1.6989700043, -1.4779000000, -1.2291000000, -0.8648000000, -0.4480000000, 0.0051800000, 0.4511080334, 0.9113744414, 0.9113744414 }; // coefs for B-spline between minPoint and midPoint (units of log luminance)
-    const half coefsHigh[10] = { 0.5154386965, 0.8470437783, 1.1358000000, 1.3802000000, 1.5197000000, 1.5985000000, 1.6467000000, 1.6746091357, 1.6878733390, 1.6878733390 }; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
-    const half2 minPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(-6.5)), 0.02); // {luminance, luminance} linear extension below this
-    const half2 midPoint = half2(segmented_spline_c5_fwd(0.18), 4.8); // {luminance, luminance}
-    const half2 maxPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(6.5)), 48.0); // {luminance, luminance} linear extension above this
-    const half slopeLow = 0.0; // log-log slope of low linear extension
-    const half slopeHigh = 0.04; // log-log slope of high linear extension
-
-    const int N_KNOTS_LOW = 8;
-    const int N_KNOTS_HIGH = 8;
-
-    // Check for negatives or zero before taking the log. If negative or zero,
-    // set to OCESMIN.
-    half xCheck = x;
-    if (xCheck <= 0.0) xCheck = 1e-4;
-
-    half logx = log10(xCheck);
-    half logy;
-
-    if (logx <= log10(minPoint.x))
-    {
-        logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
-    }
-    else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
-    {
-        half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
-        int j = knot_coord;
-        half t = knot_coord - j;
-
-        half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
-        half3 monomials = half3(t * t, t, 1.0);
-        logy = dot(monomials, mul(M, cf));
-    }
-    else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
-    {
-        half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
-        int j = knot_coord;
-        half t = knot_coord - j;
-
-        half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
-        half3 monomials = half3(t * t, t, 1.0);
-        logy = dot(monomials, mul(M, cf));
-    }
-    else
-    { //if (logIn >= log10(maxPoint.x)) {
-        logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
-    }
-
-    return pow(10.0, logy);
-}
-
-static const half RRT_GLOW_GAIN = 0.05;
-static const half RRT_GLOW_MID = 0.08;
-
-static const half RRT_RED_SCALE = 0.82;
-static const half RRT_RED_PIVOT = 0.03;
-static const half RRT_RED_HUE = 0.0;
-static const half RRT_RED_WIDTH = 135.0;
-
-static const half RRT_SAT_FACTOR = 0.96;
-
-half3 RRT(half3 aces)
-{
-    // --- Glow module --- //
-    half saturation = rgb_2_saturation(aces);
-    half ycIn = rgb_2_yc(aces);
-    half s = sigmoid_shaper((saturation - 0.4) / 0.2);
-    half addedGlow = 1.0 + glow_fwd(ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
-    aces *= addedGlow;
-
-    // --- Red modifier --- //
-    half hue = rgb_2_hue(aces);
-    half centeredHue = center_hue(hue, RRT_RED_HUE);
-    half hueWeight;
-    {
-        //hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
-        hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centeredHue / RRT_RED_WIDTH));
-        hueWeight *= hueWeight;
-    }
-
-    aces.r += hueWeight * saturation * (RRT_RED_PIVOT - aces.r) * (1.0 - RRT_RED_SCALE);
-
-    // --- ACES to RGB rendering space --- //
-    aces = clamp(aces, 0.0, HALF_MAX);  // avoids saturated negative colors from becoming positive in the matrix
-    half3 rgbPre = mul(AP0_2_AP1_MAT, aces);
-    rgbPre = clamp(rgbPre, 0, HALF_MAX);
-
-    // --- Global desaturation --- //
-    //rgbPre = mul(RRT_SAT_MAT, rgbPre);
-    rgbPre = lerp(dot(rgbPre, AP1_RGB2Y).xxx, rgbPre, RRT_SAT_FACTOR.xxx);
-
-    // --- Apply the tonescale independently in rendering-space RGB --- //
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c5_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c5_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c5_fwd(rgbPre.z);
-
-    // --- RGB rendering space to OCES --- //
-    half3 rgbOces = mul(AP1_2_AP0_MAT, rgbPost);
-
-    return rgbOces;
-}
-
-//
-// Output Device Transform
-//
-half3 Y_2_linCV(half3 Y, half Ymax, half Ymin)
-{
-    return (Y - Ymin) / (Ymax - Ymin);
-}
-
-half3 XYZ_2_xyY(half3 XYZ)
-{
-    half divisor = max(dot(XYZ, (1.0).xxx), 1e-4);
-    return half3(XYZ.xy / divisor, XYZ.y);
-}
-
-half3 xyY_2_XYZ(half3 xyY)
-{
-    half m = xyY.z / max(xyY.y, 1e-4);
-    half3 XYZ = half3(xyY.xz, (1.0 - xyY.x - xyY.y));
-    XYZ.xz *= m;
-    return XYZ;
-}
-
-static const half DIM_SURROUND_GAMMA = 0.9811;
-
-half3 darkSurround_to_dimSurround(half3 linearCV)
-{
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    half3 xyY = XYZ_2_xyY(XYZ);
-    xyY.z = clamp(xyY.z, 0.0, HALF_MAX);
-    xyY.z = pow(xyY.z, DIM_SURROUND_GAMMA);
-    XYZ = xyY_2_XYZ(xyY);
-
-    return mul(XYZ_2_AP1_MAT, XYZ);
-}
-
-half moncurve_r(half y, half gamma, half offs)
-{
-    // Reverse monitor curve
-    half x;
-    const half yb = pow(offs * gamma / ((gamma - 1.0) * (1.0 + offs)), gamma);
-    const half rs = pow((gamma - 1.0) / offs, gamma - 1.0) * pow((1.0 + offs) / gamma, gamma);
-    if (y >= yb)
-        x = (1.0 + offs) * pow(y, 1.0 / gamma) - offs;
-    else
-        x = y * rs;
-    return x;
-}
-
-half bt1886_r(half L, half gamma, half Lw, half Lb)
-{
-    // The reference EOTF specified in Rec. ITU-R BT.1886
-    // L = a(max[(V+b),0])^g
-    half a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
-    half b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
-    half V = pow(max(L / a, 0.0), 1.0 / gamma) - b;
-    return V;
-}
-
-half roll_white_fwd(
-    half x,       // color value to adjust (white scaled to around 1.0)
-    half new_wht, // white adjustment (e.g. 0.9 for 10% darkening)
-    half width    // adjusted width (e.g. 0.25 for top quarter of the tone scale)
-    )
-{
-    const half x0 = -1.0;
-    const half x1 = x0 + width;
-    const half y0 = -new_wht;
-    const half y1 = x1;
-    const half m1 = (x1 - x0);
-    const half a = y0 - y1 + m1;
-    const half b = 2.0 * (y1 - y0) - m1;
-    const half c = y0;
-    const half t = (-x - x0) / (x1 - x0);
-    half o = 0.0;
-    if (t < 0.0)
-        o = -(t * b + c);
-    else if (t > 1.0)
-        o = x;
-    else
-        o = -((t * a + b) * t + c);
-    return o;
-}
-
-half3 linear_to_sRGB(half3 x)
-{
-    return (x <= 0.0031308 ? (x * 12.9232102) : 1.055 * pow(x, 1.0 / 2.4) - 0.055);
-}
-
-half3 linear_to_bt1886(half3 x, half gamma, half Lw, half Lb)
-{
-    // Good enough approximation for now, may consider using the exact formula instead
-    // TODO: Experiment
-    return pow(max(x, 0.0), 1.0 / 2.4);
-
-    // Correct implementation (Reference EOTF specified in Rec. ITU-R BT.1886) :
-    // L = a(max[(V+b),0])^g
-    half invgamma = 1.0 / gamma;
-    half p_Lw = pow(Lw, invgamma);
-    half p_Lb = pow(Lb, invgamma);
-    half3 a = pow(p_Lw - p_Lb, gamma).xxx;
-    half3 b = (p_Lb / p_Lw - p_Lb).xxx;
-    half3 V = pow(max(x / a, 0.0), invgamma.xxx) - b;
-    return V;
-}
-
-#if defined(CUSTOM_WHITE_POINT)
-half CINEMA_WHITE;
-half CINEMA_BLACK;
-#else
-static const half CINEMA_WHITE = 48.0;
-static const half CINEMA_BLACK = CINEMA_WHITE / 2400.0;
-#endif
-
-static const half ODT_SAT_FACTOR = 0.93;
-
-// <ACEStransformID>ODT.Academy.RGBmonitor_100nits_dim.a1.0.3</ACEStransformID>
-// <ACESuserName>ACES 1.0 Output - sRGB</ACESuserName>
-
-//
-// Output Device Transform - RGB computer monitor
-//
-
-//
-// Summary :
-//  This transform is intended for mapping OCES onto a desktop computer monitor
-//  typical of those used in motion picture visual effects production. These
-//  monitors may occasionally be referred to as "sRGB" displays, however, the
-//  monitor for which this transform is designed does not exactly match the
-//  specifications in IEC 61966-2-1:1999.
-//
-//  The assumed observer adapted white is D65, and the viewing environment is
-//  that of a dim surround.
-//
-//  The monitor specified is intended to be more typical of those found in
-//  visual effects production.
-//
-// Device Primaries :
-//  Primaries are those specified in Rec. ITU-R BT.709
-//  CIE 1931 chromaticities:  x         y         Y
-//              Red:          0.64      0.33
-//              Green:        0.3       0.6
-//              Blue:         0.15      0.06
-//              White:        0.3127    0.329     100 cd/m^2
-//
-// Display EOTF :
-//  The reference electro-optical transfer function specified in
-//  IEC 61966-2-1:1999.
-//
-// Signal Range:
-//    This transform outputs full range code values.
-//
-// Assumed observer adapted white point:
-//         CIE 1931 chromaticities:    x            y
-//                                     0.3127       0.329
-//
-// Viewing Environment:
-//   This ODT has a compensation for viewing environment variables more typical
-//   of those associated with video mastering.
-//
-half3 ODT_RGBmonitor_100nits_dim(half3 oces)
-{
-    // OCES to RGB rendering space
-    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
-
-    // Apply the tonescale independently in rendering-space RGB
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
-
-    // Scale luminance to linear code value
-    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
-
-     // Apply gamma adjustment to compensate for dim surround
-    linearCV = darkSurround_to_dimSurround(linearCV);
-
-    // Apply desaturation to compensate for luminance difference
-    //linearCV = mul(ODT_SAT_MAT, linearCV);
-    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
-
-    // Convert to display primary encoding
-    // Rendering space RGB to XYZ
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    // Apply CAT from ACES white point to assumed observer adapted white point
-    XYZ = mul(D60_2_D65_CAT, XYZ);
-
-    // CIE XYZ to display primaries
-    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
-
-    // Handle out-of-gamut values
-    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
-    linearCV = saturate(linearCV);
-
-    // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
-    // with sRGB opto-electrical transfer function (OETF).
-    /*
-    // Encode linear code values with transfer function
-    half3 outputCV;
-    // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
-    const half DISPGAMMA = 2.4;
-    const half OFFSET = 0.055;
-    outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
-    outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
-    outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
-
-    outputCV = linear_to_sRGB(linearCV);
-    */
-
-    // Unity already draws to a sRGB target
-    return linearCV;
-}
-
-// <ACEStransformID>ODT.Academy.RGBmonitor_D60sim_100nits_dim.a1.0.3</ACEStransformID>
-// <ACESuserName>ACES 1.0 Output - sRGB (D60 sim.)</ACESuserName>
-
-//
-// Output Device Transform - RGB computer monitor (D60 simulation)
-//
-
-//
-// Summary :
-//  This transform is intended for mapping OCES onto a desktop computer monitor
-//  typical of those used in motion picture visual effects production. These
-//  monitors may occasionally be referred to as "sRGB" displays, however, the
-//  monitor for which this transform is designed does not exactly match the
-//  specifications in IEC 61966-2-1:1999.
-//
-//  The assumed observer adapted white is D60, and the viewing environment is
-//  that of a dim surround.
-//
-//  The monitor specified is intended to be more typical of those found in
-//  visual effects production.
-//
-// Device Primaries :
-//  Primaries are those specified in Rec. ITU-R BT.709
-//  CIE 1931 chromaticities:  x         y         Y
-//              Red:          0.64      0.33
-//              Green:        0.3       0.6
-//              Blue:         0.15      0.06
-//              White:        0.3127    0.329     100 cd/m^2
-//
-// Display EOTF :
-//  The reference electro-optical transfer function specified in
-//  IEC 61966-2-1:1999.
-//
-// Signal Range:
-//    This transform outputs full range code values.
-//
-// Assumed observer adapted white point:
-//         CIE 1931 chromaticities:    x            y
-//                                     0.32168      0.33767
-//
-// Viewing Environment:
-//   This ODT has a compensation for viewing environment variables more typical
-//   of those associated with video mastering.
-//
-half3 ODT_RGBmonitor_D60sim_100nits_dim(half3 oces)
-{
-    // OCES to RGB rendering space
-    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
-
-    // Apply the tonescale independently in rendering-space RGB
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
-
-    // Scale luminance to linear code value
-    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
-
-    // --- Compensate for different white point being darker  --- //
-    // This adjustment is to correct an issue that exists in ODTs where the device
-    // is calibrated to a white chromaticity other than D60. In order to simulate
-    // D60 on such devices, unequal code values are sent to the display to achieve
-    // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
-    // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
-    // 0.351) the red channel is higher than green and blue to compensate for the
-    // "greenish" DCI white. This is the correct behavior but it means that as
-    // highlight increase, the red channel will hit the device maximum first and
-    // clip, resulting in a chromaticity shift as the green and blue channels
-    // continue to increase.
-    // To avoid this clipping error, a slight scale factor is applied to allow the
-    // ODTs to simulate D60 within the D65 calibration white point.
-
-    // Scale and clamp white to avoid casted highlights due to D60 simulation
-    const half SCALE = 0.955;
-    linearCV = min(linearCV, 1.0) * SCALE;
-
-    // Apply gamma adjustment to compensate for dim surround
-    linearCV = darkSurround_to_dimSurround(linearCV);
-
-    // Apply desaturation to compensate for luminance difference
-    //linearCV = mul(ODT_SAT_MAT, linearCV);
-    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
-
-    // Convert to display primary encoding
-    // Rendering space RGB to XYZ
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    // CIE XYZ to display primaries
-    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
-
-    // Handle out-of-gamut values
-    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
-    linearCV = saturate(linearCV);
-
-    // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
-    // with sRGB opto-electrical transfer function (OETF).
-    /*
-    // Encode linear code values with transfer function
-    half3 outputCV;
-    // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
-    const half DISPGAMMA = 2.4;
-    const half OFFSET = 0.055;
-    outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
-    outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
-    outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
-
-    outputCV = linear_to_sRGB(linearCV);
-    */
-
-    // Unity already draws to a sRGB target
-    return linearCV;
-}
-
-// <ACEStransformID>ODT.Academy.Rec709_100nits_dim.a1.0.3</ACEStransformID>
-// <ACESuserName>ACES 1.0 Output - Rec.709</ACESuserName>
-
-//
-// Output Device Transform - Rec709
-//
-
-//
-// Summary :
-//  This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
-//  that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
-//  adapted white is D65, and the viewing environment is a dim surround.
-//
-//  A possible use case for this transform would be HDTV/video mastering.
-//
-// Device Primaries :
-//  Primaries are those specified in Rec. ITU-R BT.709
-//  CIE 1931 chromaticities:  x         y         Y
-//              Red:          0.64      0.33
-//              Green:        0.3       0.6
-//              Blue:         0.15      0.06
-//              White:        0.3127    0.329     100 cd/m^2
-//
-// Display EOTF :
-//  The reference electro-optical transfer function specified in
-//  Rec. ITU-R BT.1886.
-//
-// Signal Range:
-//    By default, this transform outputs full range code values. If instead a
-//    SMPTE "legal" signal is desired, there is a runtime flag to output
-//    SMPTE legal signal. In ctlrender, this can be achieved by appending
-//    '-param1 legalRange 1' after the '-ctl odt.ctl' string.
-//
-// Assumed observer adapted white point:
-//         CIE 1931 chromaticities:    x            y
-//                                     0.3127       0.329
-//
-// Viewing Environment:
-//   This ODT has a compensation for viewing environment variables more typical
-//   of those associated with video mastering.
-//
-half3 ODT_Rec709_100nits_dim(half3 oces)
-{
-    // OCES to RGB rendering space
-    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
-
-    // Apply the tonescale independently in rendering-space RGB
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
-
-    // Scale luminance to linear code value
-    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
-
-    // Apply gamma adjustment to compensate for dim surround
-    linearCV = darkSurround_to_dimSurround(linearCV);
-
-    // Apply desaturation to compensate for luminance difference
-    //linearCV = mul(ODT_SAT_MAT, linearCV);
-    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
-
-    // Convert to display primary encoding
-    // Rendering space RGB to XYZ
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    // Apply CAT from ACES white point to assumed observer adapted white point
-    XYZ = mul(D60_2_D65_CAT, XYZ);
-
-    // CIE XYZ to display primaries
-    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
-
-    // Handle out-of-gamut values
-    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
-    linearCV = saturate(linearCV);
-
-    // Encode linear code values with transfer function
-    const half DISPGAMMA = 2.4;
-    const half L_W = 1.0;
-    const half L_B = 0.0;
-    half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
-
-    // TODO: Implement support for legal range.
-
-    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
-    // by default which will result in double perceptual encoding, thus for now if one want to use
-    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
-    // compensate for Unity default behaviour.
-
-    return outputCV;
-}
-
-// <ACEStransformID>ODT.Academy.Rec709_D60sim_100nits_dim.a1.0.3</ACEStransformID>
-// <ACESuserName>ACES 1.0 Output - Rec.709 (D60 sim.)</ACESuserName>
-
-//
-// Output Device Transform - Rec709 (D60 simulation)
-//
-
-//
-// Summary :
-//  This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
-//  that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
-//  adapted white is D60, and the viewing environment is a dim surround.
-//
-//  A possible use case for this transform would be cinema "soft-proofing".
-//
-// Device Primaries :
-//  Primaries are those specified in Rec. ITU-R BT.709
-//  CIE 1931 chromaticities:  x         y         Y
-//              Red:          0.64      0.33
-//              Green:        0.3       0.6
-//              Blue:         0.15      0.06
-//              White:        0.3127    0.329     100 cd/m^2
-//
-// Display EOTF :
-//  The reference electro-optical transfer function specified in
-//  Rec. ITU-R BT.1886.
-//
-// Signal Range:
-//    By default, this transform outputs full range code values. If instead a
-//    SMPTE "legal" signal is desired, there is a runtime flag to output
-//    SMPTE legal signal. In ctlrender, this can be achieved by appending
-//    '-param1 legalRange 1' after the '-ctl odt.ctl' string.
-//
-// Assumed observer adapted white point:
-//         CIE 1931 chromaticities:    x            y
-//                                     0.32168      0.33767
-//
-// Viewing Environment:
-//   This ODT has a compensation for viewing environment variables more typical
-//   of those associated with video mastering.
-//
-half3 ODT_Rec709_D60sim_100nits_dim(half3 oces)
-{
-    // OCES to RGB rendering space
-    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
-
-    // Apply the tonescale independently in rendering-space RGB
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
-
-    // Scale luminance to linear code value
-    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
-
-    // --- Compensate for different white point being darker  --- //
-    // This adjustment is to correct an issue that exists in ODTs where the device
-    // is calibrated to a white chromaticity other than D60. In order to simulate
-    // D60 on such devices, unequal code values must be sent to the display to achieve
-    // the chromaticities of D60. More specifically, in order to produce D60 on a device
-    // calibrated to a D65 white point (i.e. equal code values yield CIE x,y
-    // chromaticities of 0.3127, 0.329) the red channel must be slightly higher than
-    // that of green and blue in order to compensate for the relatively more "blue-ish"
-    // D65 white. This unequalness of color channels is the correct behavior but it
-    // means that as neutral highlights increase, the red channel will hit the
-    // device maximum first and clip, resulting in a small chromaticity shift as the
-    // green and blue channels continue to increase to their maximums.
-    // To avoid this clipping error, a slight scale factor is applied to allow the
-    // ODTs to simulate D60 within the D65 calibration white point.
-
-    // Scale and clamp white to avoid casted highlights due to D60 simulation
-    const half SCALE = 0.955;
-    linearCV = min(linearCV, 1.0) * SCALE;
-
-    // Apply gamma adjustment to compensate for dim surround
-    linearCV = darkSurround_to_dimSurround(linearCV);
-
-    // Apply desaturation to compensate for luminance difference
-    //linearCV = mul(ODT_SAT_MAT, linearCV);
-    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
-
-    // Convert to display primary encoding
-    // Rendering space RGB to XYZ
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    // CIE XYZ to display primaries
-    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
-
-    // Handle out-of-gamut values
-    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
-    linearCV = saturate(linearCV);
-
-    // Encode linear code values with transfer function
-    const half DISPGAMMA = 2.4;
-    const half L_W = 1.0;
-    const half L_B = 0.0;
-    half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
-
-    // TODO: Implement support for legal range.
-
-    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
-    // by default which will result in double perceptual encoding, thus for now if one want to use
-    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
-    // compensate for Unity default behaviour.
-
-    return outputCV;
-}
-
-// <ACEStransformID>ODT.Academy.Rec2020_100nits_dim.a1.0.3</ACEStransformID>
-// <ACESuserName>ACES 1.0 Output - Rec.2020</ACESuserName>
-
-//
-// Output Device Transform - Rec2020
-//
-
-//
-// Summary :
-//  This transform is intended for mapping OCES onto a Rec.2020 broadcast
-//  monitor that is calibrated to a D65 white point at 100 cd/m^2. The assumed
-//  observer adapted white is D65, and the viewing environment is that of a dim
-//  surround.
-//
-//  A possible use case for this transform would be UHDTV/video mastering.
-//
-// Device Primaries :
-//  Primaries are those specified in Rec. ITU-R BT.2020
-//  CIE 1931 chromaticities:  x         y         Y
-//              Red:          0.708     0.292
-//              Green:        0.17      0.797
-//              Blue:         0.131     0.046
-//              White:        0.3127    0.329     100 cd/m^2
-//
-// Display EOTF :
-//  The reference electro-optical transfer function specified in
-//  Rec. ITU-R BT.1886.
-//
-// Signal Range:
-//    By default, this transform outputs full range code values. If instead a
-//    SMPTE "legal" signal is desired, there is a runtime flag to output
-//    SMPTE legal signal. In ctlrender, this can be achieved by appending
-//    '-param1 legalRange 1' after the '-ctl odt.ctl' string.
-//
-// Assumed observer adapted white point:
-//         CIE 1931 chromaticities:    x            y
-//                                     0.3127       0.329
-//
-// Viewing Environment:
-//   This ODT has a compensation for viewing environment variables more typical
-//   of those associated with video mastering.
-//
-
-half3 ODT_Rec2020_100nits_dim(half3 oces)
-{
-    // OCES to RGB rendering space
-    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
-
-    // Apply the tonescale independently in rendering-space RGB
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
-
-    // Scale luminance to linear code value
-    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
-
-    // Apply gamma adjustment to compensate for dim surround
-    linearCV = darkSurround_to_dimSurround(linearCV);
-
-    // Apply desaturation to compensate for luminance difference
-    //linearCV = mul(ODT_SAT_MAT, linearCV);
-    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
-
-    // Convert to display primary encoding
-    // Rendering space RGB to XYZ
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    // Apply CAT from ACES white point to assumed observer adapted white point
-    XYZ = mul(D60_2_D65_CAT, XYZ);
-
-    // CIE XYZ to display primaries
-    linearCV = mul(XYZ_2_REC2020_MAT, XYZ);
-
-    // Handle out-of-gamut values
-    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
-    linearCV = saturate(linearCV);
-
-    // Encode linear code values with transfer function
-    const half DISPGAMMA = 2.4;
-    const half L_W = 1.0;
-    const half L_B = 0.0;
-    half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
-
-    // TODO: Implement support for legal range.
-
-    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
-    // by default which will result in double perceptual encoding, thus for now if one want to use
-    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
-    // compensate for Unity default behaviour.
-
-    return outputCV;
-}
-
-// <ACEStransformID>ODT.Academy.P3DCI_48nits.a1.0.3</ACEStransformID>
-// <ACESuserName>ACES 1.0 Output - P3-DCI</ACESuserName>
-
-//
-// Output Device Transform - P3DCI (D60 Simulation)
-//
-
-//
-// Summary :
-//  This transform is intended for mapping OCES onto a P3 digital cinema
-//  projector that is calibrated to a DCI white point at 48 cd/m^2. The assumed
-//  observer adapted white is D60, and the viewing environment is that of a dark
-//  theater.
-//
-// Device Primaries :
-//  CIE 1931 chromaticities:  x         y         Y
-//              Red:          0.68      0.32
-//              Green:        0.265     0.69
-//              Blue:         0.15      0.06
-//              White:        0.314     0.351     48 cd/m^2
-//
-// Display EOTF :
-//  Gamma: 2.6
-//
-// Assumed observer adapted white point:
-//         CIE 1931 chromaticities:    x            y
-//                                     0.32168      0.33767
-//
-// Viewing Environment:
-//  Environment specified in SMPTE RP 431-2-2007
-//
-half3 ODT_P3DCI_48nits(half3 oces)
-{
-    // OCES to RGB rendering space
-    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
-
-    // Apply the tonescale independently in rendering-space RGB
-    half3 rgbPost;
-    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
-    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
-    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
-
-    // Scale luminance to linear code value
-    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
-
-    // --- Compensate for different white point being darker  --- //
-    // This adjustment is to correct an issue that exists in ODTs where the device
-    // is calibrated to a white chromaticity other than D60. In order to simulate
-    // D60 on such devices, unequal code values are sent to the display to achieve
-    // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
-    // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
-    // 0.351) the red channel is higher than green and blue to compensate for the
-    // "greenish" DCI white. This is the correct behavior but it means that as
-    // highlight increase, the red channel will hit the device maximum first and
-    // clip, resulting in a chromaticity shift as the green and blue channels
-    // continue to increase.
-    // To avoid this clipping error, a slight scale factor is applied to allow the
-    // ODTs to simulate D60 within the D65 calibration white point. However, the
-    // magnitude of the scale factor required for the P3DCI ODT was considered too
-    // large. Therefore, the scale factor was reduced and the additional required
-    // compression was achieved via a reshaping of the highlight rolloff in
-    // conjunction with the scale. The shape of this rolloff was determined
-    // throught subjective experiments and deemed to best reproduce the
-    // "character" of the highlights in the P3D60 ODT.
-
-    // Roll off highlights to avoid need for as much scaling
-    const half NEW_WHT = 0.918;
-    const half ROLL_WIDTH = 0.5;
-    linearCV.x = roll_white_fwd(linearCV.x, NEW_WHT, ROLL_WIDTH);
-    linearCV.y = roll_white_fwd(linearCV.y, NEW_WHT, ROLL_WIDTH);
-    linearCV.z = roll_white_fwd(linearCV.z, NEW_WHT, ROLL_WIDTH);
-
-    // Scale and clamp white to avoid casted highlights due to D60 simulation
-    const half SCALE = 0.96;
-    linearCV = min(linearCV, NEW_WHT) * SCALE;
-
-    // Convert to display primary encoding
-    // Rendering space RGB to XYZ
-    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
-
-    // CIE XYZ to display primaries
-    linearCV = mul(XYZ_2_DCIP3_MAT, XYZ);
-
-    // Handle out-of-gamut values
-    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
-    linearCV = saturate(linearCV);
-
-    // Encode linear code values with transfer function
-    const half DISPGAMMA = 2.6;
-    half3 outputCV = pow(linearCV, 1.0 / DISPGAMMA);
-
-    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
-    // by default which will result in double perceptual encoding, thus for now if one want to use
-    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
-    // compensate for Unity default behaviour.
-
-    return outputCV;
-}
-
-#endif // __ACES__
+#ifndef __ACES__
+#define __ACES__
+
+/**
+ * https://github.com/ampas/aces-dev
+ *
+ * Academy Color Encoding System (ACES) software and tools are provided by the
+ * Academy under the following terms and conditions: A worldwide, royalty-free,
+ * non-exclusive right to copy, modify, create derivatives, and use, in source and
+ * binary forms, is hereby granted, subject to acceptance of this license.
+ *
+ * Copyright 2015 Academy of Motion Picture Arts and Sciences (A.M.P.A.S.).
+ * Portions contributed by others as indicated. All rights reserved.
+ *
+ * Performance of any of the aforementioned acts indicates acceptance to be bound
+ * by the following terms and conditions:
+ *
+ * * Copies of source code, in whole or in part, must retain the above copyright
+ * notice, this list of conditions and the Disclaimer of Warranty.
+ *
+ * * Use in binary form must retain the above copyright notice, this list of
+ * conditions and the Disclaimer of Warranty in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * * Nothing in this license shall be deemed to grant any rights to trademarks,
+ * copyrights, patents, trade secrets or any other intellectual property of
+ * A.M.P.A.S. or any contributors, except as expressly stated herein.
+ *
+ * * Neither the name "A.M.P.A.S." nor the name of any other contributors to this
+ * software may be used to endorse or promote products derivative of or based on
+ * this software without express prior written permission of A.M.P.A.S. or the
+ * contributors, as appropriate.
+ *
+ * This license shall be construed pursuant to the laws of the State of
+ * California, and any disputes related thereto shall be subject to the
+ * jurisdiction of the courts therein.
+ *
+ * Disclaimer of Warranty: THIS SOFTWARE IS PROVIDED BY A.M.P.A.S. AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL A.M.P.A.S., OR ANY
+ * CONTRIBUTORS OR DISTRIBUTORS, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, RESITUTIONARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE ACADEMY SPECIFICALLY
+ * DISCLAIMS ANY REPRESENTATIONS OR WARRANTIES WHATSOEVER RELATED TO PATENT OR
+ * OTHER INTELLECTUAL PROPERTY RIGHTS IN THE ACADEMY COLOR ENCODING SYSTEM, OR
+ * APPLICATIONS THEREOF, HELD BY PARTIES OTHER THAN A.M.P.A.S.,WHETHER DISCLOSED OR
+ * UNDISCLOSED.
+ */
+
+//#define CUSTOM_WHITE_POINT
+
+/*
+    Basic usage :
+
+    half4 color = tex2D(_MainTex, i.uv);
+    half3 aces = unity_to_ACES(color.rgb);
+    half3 oces = RRT(aces);
+    half3 odt = ODT_RGBmonitor_100nits_dim(oces);
+    return half4(odt, color.a);
+
+    If you want to customize the white point, uncomment the previous define and set uniforms accordingly:
+
+    float whitePoint = 48f; // Default ACES value
+    material.SetFloat("CINEMA_WHITE", whitePoint);
+    material.SetFloat("CINEMA_DARK", whitePoint / 2400f);
+ */
+
+#include "Common.cginc"
+
+#define ACEScc_MAX      1.4679964
+#define ACEScc_MIDGRAY  0.4135884
+
+//
+// Precomputed matrices (pre-transposed)
+// See https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
+//
+static const half3x3 sRGB_2_AP0 = {
+    0.4397010, 0.3829780, 0.1773350,
+    0.0897923, 0.8134230, 0.0967616,
+    0.0175440, 0.1115440, 0.8707040
+};
+
+static const half3x3 sRGB_2_AP1 = {
+    0.61319, 0.33951, 0.04737,
+    0.07021, 0.91634, 0.01345,
+    0.02062, 0.10957, 0.86961
+};
+
+static const half3x3 AP0_2_sRGB = {
+    2.52169, -1.13413, -0.38756,
+    -0.27648, 1.37272, -0.09624,
+    -0.01538, -0.15298, 1.16835,
+};
+
+static const half3x3 AP1_2_sRGB = {
+    1.70505, -0.62179, -0.08326,
+    -0.13026, 1.14080, -0.01055,
+    -0.02400, -0.12897, 1.15297,
+};
+
+static const half3x3 AP0_2_AP1_MAT = {
+     1.4514393161, -0.2365107469, -0.2149285693,
+    -0.0765537734,  1.1762296998, -0.0996759264,
+     0.0083161484, -0.0060324498,  0.9977163014
+};
+
+static const half3x3 AP1_2_AP0_MAT = {
+     0.6954522414, 0.1406786965, 0.1638690622,
+     0.0447945634, 0.8596711185, 0.0955343182,
+    -0.0055258826, 0.0040252103, 1.0015006723
+};
+
+static const half3x3 AP1_2_XYZ_MAT = {
+     0.6624541811, 0.1340042065, 0.1561876870,
+     0.2722287168, 0.6740817658, 0.0536895174,
+    -0.0055746495, 0.0040607335, 1.0103391003
+};
+
+static const half3x3 XYZ_2_AP1_MAT = {
+     1.6410233797, -0.3248032942, -0.2364246952,
+    -0.6636628587,  1.6153315917,  0.0167563477,
+     0.0117218943, -0.0082844420,  0.9883948585
+};
+
+static const half3x3 XYZ_2_REC709_MAT = {
+     3.2409699419, -1.5373831776, -0.4986107603,
+    -0.9692436363,  1.8759675015,  0.0415550574,
+     0.0556300797, -0.2039769589,  1.0569715142
+};
+
+static const half3x3 XYZ_2_REC2020_MAT = {
+     1.7166511880, -0.3556707838, -0.2533662814,
+    -0.6666843518,  1.6164812366,  0.0157685458,
+     0.0176398574, -0.0427706133,  0.9421031212
+};
+
+static const half3x3 XYZ_2_DCIP3_MAT = {
+     2.7253940305, -1.0180030062, -0.4401631952,
+    -0.7951680258,  1.6897320548,  0.0226471906,
+     0.0412418914, -0.0876390192,  1.1009293786
+};
+
+static const half3 AP1_RGB2Y = half3(0.272229, 0.674082, 0.0536895);
+
+static const half3x3 RRT_SAT_MAT = {
+    0.9708890, 0.0269633, 0.00214758,
+    0.0108892, 0.9869630, 0.00214758,
+    0.0108892, 0.0269633, 0.96214800
+};
+
+static const half3x3 ODT_SAT_MAT = {
+    0.949056, 0.0471857, 0.00375827,
+    0.019056, 0.9771860, 0.00375827,
+    0.019056, 0.0471857, 0.93375800
+};
+
+static const half3x3 D60_2_D65_CAT = {
+     0.98722400, -0.00611327, 0.0159533,
+    -0.00759836,  1.00186000, 0.0053302,
+     0.00307257, -0.00509595, 1.0816800
+};
+
+//
+// Unity to ACES
+//
+// converts Unity raw (sRGB primaries) to
+//          ACES2065-1 (AP0 w/ linear encoding)
+//
+half3 unity_to_ACES(half3 x)
+{
+    x = mul(sRGB_2_AP0, x);
+    return x;
+}
+
+//
+// ACES to Unity
+//
+// converts ACES2065-1 (AP0 w/ linear encoding)
+//          Unity raw (sRGB primaries) to
+//
+half3 ACES_to_unity(half3 x)
+{
+    x = mul(AP0_2_sRGB, x);
+    return x;
+}
+
+//
+// Unity to ACEScg
+//
+// converts Unity raw (sRGB primaries) to
+//          ACEScg (AP1 w/ linear encoding)
+//
+half3 unity_to_ACEScg(half3 x)
+{
+    x = mul(sRGB_2_AP1, x);
+    return x;
+}
+
+//
+// ACEScg to Unity
+//
+// converts ACEScg (AP1 w/ linear encoding) to
+//          Unity raw (sRGB primaries)
+//
+half3 ACEScg_to_unity(half3 x)
+{
+    x = mul(AP1_2_sRGB, x);
+    return x;
+}
+
+//
+// ACES Color Space Conversion - ACES to ACEScc
+//
+// converts ACES2065-1 (AP0 w/ linear encoding) to
+//          ACEScc (AP1 w/ logarithmic encoding)
+//
+// This transform follows the formulas from section 4.4 in S-2014-003
+//
+half ACES_to_ACEScc(half x)
+{
+    if (x <= 0.0)
+        return -0.35828683; // = (log2(pow(2.0, -15.0) * 0.5) + 9.72) / 17.52
+    else if (x < pow(2.0, -15.0))
+        return (log2(pow(2.0, -16.0) + x * 0.5) + 9.72) / 17.52;
+    else // (x >= pow(2.0, -15.0))
+        return (log2(x) + 9.72) / 17.52;
+}
+
+half3 ACES_to_ACEScc(half3 x)
+{
+    x = clamp(x, 0.0, HALF_MAX);
+
+    // x is clamped to [0, HALF_MAX], skip the <= 0 check
+    return (x < 0.00003051757) ? (log2(0.00001525878 + x * 0.5) + 9.72) / 17.52 : (log2(x) + 9.72) / 17.52;
+
+    /*
+    return half3(
+        ACES_to_ACEScc(x.r),
+        ACES_to_ACEScc(x.g),
+        ACES_to_ACEScc(x.b)
+    );
+    */
+}
+
+//
+// ACES Color Space Conversion - ACEScc to ACES
+//
+// converts ACEScc (AP1 w/ ACESlog encoding) to
+//          ACES2065-1 (AP0 w/ linear encoding)
+//
+// This transform follows the formulas from section 4.4 in S-2014-003
+//
+half ACEScc_to_ACES(half x)
+{
+    // TODO: Optimize me
+    if (x < -0.3013698630) // (9.72 - 15) / 17.52
+        return (pow(2.0, x * 17.52 - 9.72) - pow(2.0, -16.0)) * 2.0;
+    else if (x < (log2(HALF_MAX) + 9.72) / 17.52)
+        return pow(2.0, x * 17.52 - 9.72);
+    else // (x >= (log2(HALF_MAX) + 9.72) / 17.52)
+        return HALF_MAX;
+}
+
+half3 ACEScc_to_ACES(half3 x)
+{
+    return half3(
+        ACEScc_to_ACES(x.r),
+        ACEScc_to_ACES(x.g),
+        ACEScc_to_ACES(x.b)
+    );
+}
+
+//
+// ACES Color Space Conversion - ACES to ACEScg
+//
+// converts ACES2065-1 (AP0 w/ linear encoding) to
+//          ACEScg (AP1 w/ linear encoding)
+//
+half3 ACES_to_ACEScg(half3 x)
+{
+    return mul(AP0_2_AP1_MAT, x);
+}
+
+//
+// ACES Color Space Conversion - ACEScg to ACES
+//
+// converts ACEScg (AP1 w/ linear encoding) to
+//          ACES2065-1 (AP0 w/ linear encoding)
+//
+half3 ACEScg_to_ACES(half3 x)
+{
+    return mul(AP1_2_AP0_MAT, x);
+}
+
+//
+// Reference Rendering Transform (RRT)
+//
+//   Input is ACES
+//   Output is OCES
+//
+half rgb_2_saturation(half3 rgb)
+{
+    const half TINY = 1e-10;
+    half mi = Min3(rgb);
+    half ma = Max3(rgb);
+    return (max(ma, TINY) - max(mi, TINY)) / max(ma, 1e-2);
+}
+
+half rgb_2_yc(half3 rgb)
+{
+    const half ycRadiusWeight = 1.75;
+
+    // Converts RGB to a luminance proxy, here called YC
+    // YC is ~ Y + K * Chroma
+    // Constant YC is a cone-shaped surface in RGB space, with the tip on the
+    // neutral axis, towards white.
+    // YC is normalized: RGB 1 1 1 maps to YC = 1
+    //
+    // ycRadiusWeight defaults to 1.75, although can be overridden in function
+    // call to rgb_2_yc
+    // ycRadiusWeight = 1 -> YC for pure cyan, magenta, yellow == YC for neutral
+    // of same value
+    // ycRadiusWeight = 2 -> YC for pure red, green, blue  == YC for  neutral of
+    // same value.
+
+    half r = rgb.x;
+    half g = rgb.y;
+    half b = rgb.z;
+    half chroma = sqrt(b * (b - g) + g * (g - r) + r * (r - b));
+    return (b + g + r + ycRadiusWeight * chroma) / 3.0;
+}
+
+half rgb_2_hue(half3 rgb)
+{
+    // Returns a geometric hue angle in degrees (0-360) based on RGB values.
+    // For neutral colors, hue is undefined and the function will return a quiet NaN value.
+    half hue;
+    if (rgb.x == rgb.y && rgb.y == rgb.z)
+        hue = 0.0; // RGB triplets where RGB are equal have an undefined hue
+    else
+        hue = (180.0 / UNITY_PI) * atan2(sqrt(3.0) * (rgb.y - rgb.z), 2.0 * rgb.x - rgb.y - rgb.z);
+
+    if (hue < 0.0) hue = hue + 360.0;
+
+    return hue;
+}
+
+half center_hue(half hue, half centerH)
+{
+    half hueCentered = hue - centerH;
+    if (hueCentered < -180.0) hueCentered = hueCentered + 360.0;
+    else if (hueCentered > 180.0) hueCentered = hueCentered - 360.0;
+    return hueCentered;
+}
+
+half sigmoid_shaper(half x)
+{
+    // Sigmoid function in the range 0 to 1 spanning -2 to +2.
+
+    half t = max(1.0 - abs(x / 2.0), 0.0);
+    half y = 1.0 + sign(x) * (1.0 - t * t);
+
+    return y / 2.0;
+}
+
+half glow_fwd(half ycIn, half glowGainIn, half glowMid)
+{
+    half glowGainOut;
+
+    if (ycIn <= 2.0 / 3.0 * glowMid)
+        glowGainOut = glowGainIn;
+    else if (ycIn >= 2.0 * glowMid)
+        glowGainOut = 0.0;
+    else
+        glowGainOut = glowGainIn * (glowMid / ycIn - 1.0 / 2.0);
+
+    return glowGainOut;
+}
+
+/*
+half cubic_basis_shaper
+(
+    half x,
+    half w   // full base width of the shaper function (in degrees)
+)
+{
+    half M[4][4] = {
+        { -1.0 / 6,  3.0 / 6, -3.0 / 6,  1.0 / 6 },
+        {  3.0 / 6, -6.0 / 6,  3.0 / 6,  0.0 / 6 },
+        { -3.0 / 6,  0.0 / 6,  3.0 / 6,  0.0 / 6 },
+        {  1.0 / 6,  4.0 / 6,  1.0 / 6,  0.0 / 6 }
+    };
+
+    half knots[5] = {
+        -w / 2.0,
+        -w / 4.0,
+             0.0,
+         w / 4.0,
+         w / 2.0
+    };
+
+    half y = 0.0;
+    if ((x > knots[0]) && (x < knots[4]))
+    {
+        half knot_coord = (x - knots[0]) * 4.0 / w;
+        int j = knot_coord;
+        half t = knot_coord - j;
+
+        half monomials[4] = { t*t*t, t*t, t, 1.0 };
+
+        // (if/else structure required for compatibility with CTL < v1.5.)
+        if (j == 3)
+        {
+            y = monomials[0] * M[0][0] + monomials[1] * M[1][0] +
+                monomials[2] * M[2][0] + monomials[3] * M[3][0];
+        }
+        else if (j == 2)
+        {
+            y = monomials[0] * M[0][1] + monomials[1] * M[1][1] +
+                monomials[2] * M[2][1] + monomials[3] * M[3][1];
+        }
+        else if (j == 1)
+        {
+            y = monomials[0] * M[0][2] + monomials[1] * M[1][2] +
+                monomials[2] * M[2][2] + monomials[3] * M[3][2];
+        }
+        else if (j == 0)
+        {
+            y = monomials[0] * M[0][3] + monomials[1] * M[1][3] +
+                monomials[2] * M[2][3] + monomials[3] * M[3][3];
+        }
+        else
+        {
+            y = 0.0;
+        }
+    }
+
+    return y * 3.0 / 2.0;
+}
+*/
+
+static const half3x3 M = {
+     0.5, -1.0, 0.5,
+    -1.0,  1.0, 0.0,
+     0.5,  0.5, 0.0
+};
+
+half segmented_spline_c5_fwd(half x)
+{
+    const half coefsLow[6] = { -4.0000000000, -4.0000000000, -3.1573765773, -0.4852499958, 1.8477324706, 1.8477324706 }; // coefs for B-spline between minPoint and midPoint (units of log luminance)
+    const half coefsHigh[6] = { -0.7185482425, 2.0810307172, 3.6681241237, 4.0000000000, 4.0000000000, 4.0000000000 }; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
+    const half2 minPoint = half2(0.18 * exp2(-15.0), 0.0001); // {luminance, luminance} linear extension below this
+    const half2 midPoint = half2(0.18, 0.48); // {luminance, luminance}
+    const half2 maxPoint = half2(0.18 * exp2(18.0), 10000.0); // {luminance, luminance} linear extension above this
+    const half slopeLow = 0.0; // log-log slope of low linear extension
+    const half slopeHigh = 0.0; // log-log slope of high linear extension
+
+    const int N_KNOTS_LOW = 4;
+    const int N_KNOTS_HIGH = 4;
+
+    // Check for negatives or zero before taking the log. If negative or zero,
+    // set to ACESMIN.1
+    float xCheck = x;
+    if (xCheck <= 0.0) xCheck = 0.00006103515; // = pow(2.0, -14.0);
+
+    half logx = log10(xCheck);
+    half logy;
+
+    if (logx <= log10(minPoint.x))
+    {
+        logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
+    }
+    else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
+    {
+        half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
+        int j = knot_coord;
+        half t = knot_coord - j;
+
+        half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
+        half3 monomials = half3(t * t, t, 1.0);
+        logy = dot(monomials, mul(M, cf));
+    }
+    else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
+    {
+        half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
+        int j = knot_coord;
+        half t = knot_coord - j;
+
+        half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
+        half3 monomials = half3(t * t, t, 1.0);
+        logy = dot(monomials, mul(M, cf));
+    }
+    else
+    { //if (logIn >= log10(maxPoint.x)) {
+        logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
+    }
+
+    return pow(10.0, logy);
+}
+
+half segmented_spline_c9_fwd(half x)
+{
+    const half coefsLow[10] = { -1.6989700043, -1.6989700043, -1.4779000000, -1.2291000000, -0.8648000000, -0.4480000000, 0.0051800000, 0.4511080334, 0.9113744414, 0.9113744414 }; // coefs for B-spline between minPoint and midPoint (units of log luminance)
+    const half coefsHigh[10] = { 0.5154386965, 0.8470437783, 1.1358000000, 1.3802000000, 1.5197000000, 1.5985000000, 1.6467000000, 1.6746091357, 1.6878733390, 1.6878733390 }; // coefs for B-spline between midPoint and maxPoint (units of log luminance)
+    const half2 minPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(-6.5)), 0.02); // {luminance, luminance} linear extension below this
+    const half2 midPoint = half2(segmented_spline_c5_fwd(0.18), 4.8); // {luminance, luminance}
+    const half2 maxPoint = half2(segmented_spline_c5_fwd(0.18 * exp2(6.5)), 48.0); // {luminance, luminance} linear extension above this
+    const half slopeLow = 0.0; // log-log slope of low linear extension
+    const half slopeHigh = 0.04; // log-log slope of high linear extension
+
+    const int N_KNOTS_LOW = 8;
+    const int N_KNOTS_HIGH = 8;
+
+    // Check for negatives or zero before taking the log. If negative or zero,
+    // set to OCESMIN.
+    half xCheck = x;
+    if (xCheck <= 0.0) xCheck = 1e-4;
+
+    half logx = log10(xCheck);
+    half logy;
+
+    if (logx <= log10(minPoint.x))
+    {
+        logy = logx * slopeLow + (log10(minPoint.y) - slopeLow * log10(minPoint.x));
+    }
+    else if ((logx > log10(minPoint.x)) && (logx < log10(midPoint.x)))
+    {
+        half knot_coord = (N_KNOTS_LOW - 1) * (logx - log10(minPoint.x)) / (log10(midPoint.x) - log10(minPoint.x));
+        int j = knot_coord;
+        half t = knot_coord - j;
+
+        half3 cf = half3(coefsLow[j], coefsLow[j + 1], coefsLow[j + 2]);
+        half3 monomials = half3(t * t, t, 1.0);
+        logy = dot(monomials, mul(M, cf));
+    }
+    else if ((logx >= log10(midPoint.x)) && (logx < log10(maxPoint.x)))
+    {
+        half knot_coord = (N_KNOTS_HIGH - 1) * (logx - log10(midPoint.x)) / (log10(maxPoint.x) - log10(midPoint.x));
+        int j = knot_coord;
+        half t = knot_coord - j;
+
+        half3 cf = half3(coefsHigh[j], coefsHigh[j + 1], coefsHigh[j + 2]);
+        half3 monomials = half3(t * t, t, 1.0);
+        logy = dot(monomials, mul(M, cf));
+    }
+    else
+    { //if (logIn >= log10(maxPoint.x)) {
+        logy = logx * slopeHigh + (log10(maxPoint.y) - slopeHigh * log10(maxPoint.x));
+    }
+
+    return pow(10.0, logy);
+}
+
+static const half RRT_GLOW_GAIN = 0.05;
+static const half RRT_GLOW_MID = 0.08;
+
+static const half RRT_RED_SCALE = 0.82;
+static const half RRT_RED_PIVOT = 0.03;
+static const half RRT_RED_HUE = 0.0;
+static const half RRT_RED_WIDTH = 135.0;
+
+static const half RRT_SAT_FACTOR = 0.96;
+
+half3 RRT(half3 aces)
+{
+    // --- Glow module --- //
+    half saturation = rgb_2_saturation(aces);
+    half ycIn = rgb_2_yc(aces);
+    half s = sigmoid_shaper((saturation - 0.4) / 0.2);
+    half addedGlow = 1.0 + glow_fwd(ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
+    aces *= addedGlow;
+
+    // --- Red modifier --- //
+    half hue = rgb_2_hue(aces);
+    half centeredHue = center_hue(hue, RRT_RED_HUE);
+    half hueWeight;
+    {
+        //hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
+        hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centeredHue / RRT_RED_WIDTH));
+        hueWeight *= hueWeight;
+    }
+
+    aces.r += hueWeight * saturation * (RRT_RED_PIVOT - aces.r) * (1.0 - RRT_RED_SCALE);
+
+    // --- ACES to RGB rendering space --- //
+    aces = clamp(aces, 0.0, HALF_MAX);  // avoids saturated negative colors from becoming positive in the matrix
+    half3 rgbPre = mul(AP0_2_AP1_MAT, aces);
+    rgbPre = clamp(rgbPre, 0, HALF_MAX);
+
+    // --- Global desaturation --- //
+    //rgbPre = mul(RRT_SAT_MAT, rgbPre);
+    rgbPre = lerp(dot(rgbPre, AP1_RGB2Y).xxx, rgbPre, RRT_SAT_FACTOR.xxx);
+
+    // --- Apply the tonescale independently in rendering-space RGB --- //
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c5_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c5_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c5_fwd(rgbPre.z);
+
+    // --- RGB rendering space to OCES --- //
+    half3 rgbOces = mul(AP1_2_AP0_MAT, rgbPost);
+
+    return rgbOces;
+}
+
+//
+// Output Device Transform
+//
+half3 Y_2_linCV(half3 Y, half Ymax, half Ymin)
+{
+    return (Y - Ymin) / (Ymax - Ymin);
+}
+
+half3 XYZ_2_xyY(half3 XYZ)
+{
+    half divisor = max(dot(XYZ, (1.0).xxx), 1e-4);
+    return half3(XYZ.xy / divisor, XYZ.y);
+}
+
+half3 xyY_2_XYZ(half3 xyY)
+{
+    half m = xyY.z / max(xyY.y, 1e-4);
+    half3 XYZ = half3(xyY.xz, (1.0 - xyY.x - xyY.y));
+    XYZ.xz *= m;
+    return XYZ;
+}
+
+static const half DIM_SURROUND_GAMMA = 0.9811;
+
+half3 darkSurround_to_dimSurround(half3 linearCV)
+{
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    half3 xyY = XYZ_2_xyY(XYZ);
+    xyY.z = clamp(xyY.z, 0.0, HALF_MAX);
+    xyY.z = pow(xyY.z, DIM_SURROUND_GAMMA);
+    XYZ = xyY_2_XYZ(xyY);
+
+    return mul(XYZ_2_AP1_MAT, XYZ);
+}
+
+half moncurve_r(half y, half gamma, half offs)
+{
+    // Reverse monitor curve
+    half x;
+    const half yb = pow(offs * gamma / ((gamma - 1.0) * (1.0 + offs)), gamma);
+    const half rs = pow((gamma - 1.0) / offs, gamma - 1.0) * pow((1.0 + offs) / gamma, gamma);
+    if (y >= yb)
+        x = (1.0 + offs) * pow(y, 1.0 / gamma) - offs;
+    else
+        x = y * rs;
+    return x;
+}
+
+half bt1886_r(half L, half gamma, half Lw, half Lb)
+{
+    // The reference EOTF specified in Rec. ITU-R BT.1886
+    // L = a(max[(V+b),0])^g
+    half a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
+    half b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
+    half V = pow(max(L / a, 0.0), 1.0 / gamma) - b;
+    return V;
+}
+
+half roll_white_fwd(
+    half x,       // color value to adjust (white scaled to around 1.0)
+    half new_wht, // white adjustment (e.g. 0.9 for 10% darkening)
+    half width    // adjusted width (e.g. 0.25 for top quarter of the tone scale)
+    )
+{
+    const half x0 = -1.0;
+    const half x1 = x0 + width;
+    const half y0 = -new_wht;
+    const half y1 = x1;
+    const half m1 = (x1 - x0);
+    const half a = y0 - y1 + m1;
+    const half b = 2.0 * (y1 - y0) - m1;
+    const half c = y0;
+    const half t = (-x - x0) / (x1 - x0);
+    half o = 0.0;
+    if (t < 0.0)
+        o = -(t * b + c);
+    else if (t > 1.0)
+        o = x;
+    else
+        o = -((t * a + b) * t + c);
+    return o;
+}
+
+half3 linear_to_sRGB(half3 x)
+{
+    return (x <= 0.0031308 ? (x * 12.9232102) : 1.055 * pow(x, 1.0 / 2.4) - 0.055);
+}
+
+half3 linear_to_bt1886(half3 x, half gamma, half Lw, half Lb)
+{
+    // Good enough approximation for now, may consider using the exact formula instead
+    // TODO: Experiment
+    return pow(max(x, 0.0), 1.0 / 2.4);
+
+    // Correct implementation (Reference EOTF specified in Rec. ITU-R BT.1886) :
+    // L = a(max[(V+b),0])^g
+    half invgamma = 1.0 / gamma;
+    half p_Lw = pow(Lw, invgamma);
+    half p_Lb = pow(Lb, invgamma);
+    half3 a = pow(p_Lw - p_Lb, gamma).xxx;
+    half3 b = (p_Lb / p_Lw - p_Lb).xxx;
+    half3 V = pow(max(x / a, 0.0), invgamma.xxx) - b;
+    return V;
+}
+
+#if defined(CUSTOM_WHITE_POINT)
+half CINEMA_WHITE;
+half CINEMA_BLACK;
+#else
+static const half CINEMA_WHITE = 48.0;
+static const half CINEMA_BLACK = CINEMA_WHITE / 2400.0;
+#endif
+
+static const half ODT_SAT_FACTOR = 0.93;
+
+// <ACEStransformID>ODT.Academy.RGBmonitor_100nits_dim.a1.0.3</ACEStransformID>
+// <ACESuserName>ACES 1.0 Output - sRGB</ACESuserName>
+
+//
+// Output Device Transform - RGB computer monitor
+//
+
+//
+// Summary :
+//  This transform is intended for mapping OCES onto a desktop computer monitor
+//  typical of those used in motion picture visual effects production. These
+//  monitors may occasionally be referred to as "sRGB" displays, however, the
+//  monitor for which this transform is designed does not exactly match the
+//  specifications in IEC 61966-2-1:1999.
+//
+//  The assumed observer adapted white is D65, and the viewing environment is
+//  that of a dim surround.
+//
+//  The monitor specified is intended to be more typical of those found in
+//  visual effects production.
+//
+// Device Primaries :
+//  Primaries are those specified in Rec. ITU-R BT.709
+//  CIE 1931 chromaticities:  x         y         Y
+//              Red:          0.64      0.33
+//              Green:        0.3       0.6
+//              Blue:         0.15      0.06
+//              White:        0.3127    0.329     100 cd/m^2
+//
+// Display EOTF :
+//  The reference electro-optical transfer function specified in
+//  IEC 61966-2-1:1999.
+//
+// Signal Range:
+//    This transform outputs full range code values.
+//
+// Assumed observer adapted white point:
+//         CIE 1931 chromaticities:    x            y
+//                                     0.3127       0.329
+//
+// Viewing Environment:
+//   This ODT has a compensation for viewing environment variables more typical
+//   of those associated with video mastering.
+//
+half3 ODT_RGBmonitor_100nits_dim(half3 oces)
+{
+    // OCES to RGB rendering space
+    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
+
+    // Apply the tonescale independently in rendering-space RGB
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
+
+    // Scale luminance to linear code value
+    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
+
+     // Apply gamma adjustment to compensate for dim surround
+    linearCV = darkSurround_to_dimSurround(linearCV);
+
+    // Apply desaturation to compensate for luminance difference
+    //linearCV = mul(ODT_SAT_MAT, linearCV);
+    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
+
+    // Convert to display primary encoding
+    // Rendering space RGB to XYZ
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    // Apply CAT from ACES white point to assumed observer adapted white point
+    XYZ = mul(D60_2_D65_CAT, XYZ);
+
+    // CIE XYZ to display primaries
+    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
+
+    // Handle out-of-gamut values
+    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
+    linearCV = saturate(linearCV);
+
+    // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
+    // with sRGB opto-electrical transfer function (OETF).
+    /*
+    // Encode linear code values with transfer function
+    half3 outputCV;
+    // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
+    const half DISPGAMMA = 2.4;
+    const half OFFSET = 0.055;
+    outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
+    outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
+    outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
+
+    outputCV = linear_to_sRGB(linearCV);
+    */
+
+    // Unity already draws to a sRGB target
+    return linearCV;
+}
+
+// <ACEStransformID>ODT.Academy.RGBmonitor_D60sim_100nits_dim.a1.0.3</ACEStransformID>
+// <ACESuserName>ACES 1.0 Output - sRGB (D60 sim.)</ACESuserName>
+
+//
+// Output Device Transform - RGB computer monitor (D60 simulation)
+//
+
+//
+// Summary :
+//  This transform is intended for mapping OCES onto a desktop computer monitor
+//  typical of those used in motion picture visual effects production. These
+//  monitors may occasionally be referred to as "sRGB" displays, however, the
+//  monitor for which this transform is designed does not exactly match the
+//  specifications in IEC 61966-2-1:1999.
+//
+//  The assumed observer adapted white is D60, and the viewing environment is
+//  that of a dim surround.
+//
+//  The monitor specified is intended to be more typical of those found in
+//  visual effects production.
+//
+// Device Primaries :
+//  Primaries are those specified in Rec. ITU-R BT.709
+//  CIE 1931 chromaticities:  x         y         Y
+//              Red:          0.64      0.33
+//              Green:        0.3       0.6
+//              Blue:         0.15      0.06
+//              White:        0.3127    0.329     100 cd/m^2
+//
+// Display EOTF :
+//  The reference electro-optical transfer function specified in
+//  IEC 61966-2-1:1999.
+//
+// Signal Range:
+//    This transform outputs full range code values.
+//
+// Assumed observer adapted white point:
+//         CIE 1931 chromaticities:    x            y
+//                                     0.32168      0.33767
+//
+// Viewing Environment:
+//   This ODT has a compensation for viewing environment variables more typical
+//   of those associated with video mastering.
+//
+half3 ODT_RGBmonitor_D60sim_100nits_dim(half3 oces)
+{
+    // OCES to RGB rendering space
+    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
+
+    // Apply the tonescale independently in rendering-space RGB
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
+
+    // Scale luminance to linear code value
+    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
+
+    // --- Compensate for different white point being darker  --- //
+    // This adjustment is to correct an issue that exists in ODTs where the device
+    // is calibrated to a white chromaticity other than D60. In order to simulate
+    // D60 on such devices, unequal code values are sent to the display to achieve
+    // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
+    // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
+    // 0.351) the red channel is higher than green and blue to compensate for the
+    // "greenish" DCI white. This is the correct behavior but it means that as
+    // highlight increase, the red channel will hit the device maximum first and
+    // clip, resulting in a chromaticity shift as the green and blue channels
+    // continue to increase.
+    // To avoid this clipping error, a slight scale factor is applied to allow the
+    // ODTs to simulate D60 within the D65 calibration white point.
+
+    // Scale and clamp white to avoid casted highlights due to D60 simulation
+    const half SCALE = 0.955;
+    linearCV = min(linearCV, 1.0) * SCALE;
+
+    // Apply gamma adjustment to compensate for dim surround
+    linearCV = darkSurround_to_dimSurround(linearCV);
+
+    // Apply desaturation to compensate for luminance difference
+    //linearCV = mul(ODT_SAT_MAT, linearCV);
+    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
+
+    // Convert to display primary encoding
+    // Rendering space RGB to XYZ
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    // CIE XYZ to display primaries
+    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
+
+    // Handle out-of-gamut values
+    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
+    linearCV = saturate(linearCV);
+
+    // TODO: Revisit when it is possible to deactivate Unity default framebuffer encoding
+    // with sRGB opto-electrical transfer function (OETF).
+    /*
+    // Encode linear code values with transfer function
+    half3 outputCV;
+    // moncurve_r with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
+    const half DISPGAMMA = 2.4;
+    const half OFFSET = 0.055;
+    outputCV.x = moncurve_r(linearCV.x, DISPGAMMA, OFFSET);
+    outputCV.y = moncurve_r(linearCV.y, DISPGAMMA, OFFSET);
+    outputCV.z = moncurve_r(linearCV.z, DISPGAMMA, OFFSET);
+
+    outputCV = linear_to_sRGB(linearCV);
+    */
+
+    // Unity already draws to a sRGB target
+    return linearCV;
+}
+
+// <ACEStransformID>ODT.Academy.Rec709_100nits_dim.a1.0.3</ACEStransformID>
+// <ACESuserName>ACES 1.0 Output - Rec.709</ACESuserName>
+
+//
+// Output Device Transform - Rec709
+//
+
+//
+// Summary :
+//  This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
+//  that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
+//  adapted white is D65, and the viewing environment is a dim surround.
+//
+//  A possible use case for this transform would be HDTV/video mastering.
+//
+// Device Primaries :
+//  Primaries are those specified in Rec. ITU-R BT.709
+//  CIE 1931 chromaticities:  x         y         Y
+//              Red:          0.64      0.33
+//              Green:        0.3       0.6
+//              Blue:         0.15      0.06
+//              White:        0.3127    0.329     100 cd/m^2
+//
+// Display EOTF :
+//  The reference electro-optical transfer function specified in
+//  Rec. ITU-R BT.1886.
+//
+// Signal Range:
+//    By default, this transform outputs full range code values. If instead a
+//    SMPTE "legal" signal is desired, there is a runtime flag to output
+//    SMPTE legal signal. In ctlrender, this can be achieved by appending
+//    '-param1 legalRange 1' after the '-ctl odt.ctl' string.
+//
+// Assumed observer adapted white point:
+//         CIE 1931 chromaticities:    x            y
+//                                     0.3127       0.329
+//
+// Viewing Environment:
+//   This ODT has a compensation for viewing environment variables more typical
+//   of those associated with video mastering.
+//
+half3 ODT_Rec709_100nits_dim(half3 oces)
+{
+    // OCES to RGB rendering space
+    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
+
+    // Apply the tonescale independently in rendering-space RGB
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
+
+    // Scale luminance to linear code value
+    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
+
+    // Apply gamma adjustment to compensate for dim surround
+    linearCV = darkSurround_to_dimSurround(linearCV);
+
+    // Apply desaturation to compensate for luminance difference
+    //linearCV = mul(ODT_SAT_MAT, linearCV);
+    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
+
+    // Convert to display primary encoding
+    // Rendering space RGB to XYZ
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    // Apply CAT from ACES white point to assumed observer adapted white point
+    XYZ = mul(D60_2_D65_CAT, XYZ);
+
+    // CIE XYZ to display primaries
+    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
+
+    // Handle out-of-gamut values
+    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
+    linearCV = saturate(linearCV);
+
+    // Encode linear code values with transfer function
+    const half DISPGAMMA = 2.4;
+    const half L_W = 1.0;
+    const half L_B = 0.0;
+    half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
+
+    // TODO: Implement support for legal range.
+
+    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
+    // by default which will result in double perceptual encoding, thus for now if one want to use
+    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
+    // compensate for Unity default behaviour.
+
+    return outputCV;
+}
+
+// <ACEStransformID>ODT.Academy.Rec709_D60sim_100nits_dim.a1.0.3</ACEStransformID>
+// <ACESuserName>ACES 1.0 Output - Rec.709 (D60 sim.)</ACESuserName>
+
+//
+// Output Device Transform - Rec709 (D60 simulation)
+//
+
+//
+// Summary :
+//  This transform is intended for mapping OCES onto a Rec.709 broadcast monitor
+//  that is calibrated to a D65 white point at 100 cd/m^2. The assumed observer
+//  adapted white is D60, and the viewing environment is a dim surround.
+//
+//  A possible use case for this transform would be cinema "soft-proofing".
+//
+// Device Primaries :
+//  Primaries are those specified in Rec. ITU-R BT.709
+//  CIE 1931 chromaticities:  x         y         Y
+//              Red:          0.64      0.33
+//              Green:        0.3       0.6
+//              Blue:         0.15      0.06
+//              White:        0.3127    0.329     100 cd/m^2
+//
+// Display EOTF :
+//  The reference electro-optical transfer function specified in
+//  Rec. ITU-R BT.1886.
+//
+// Signal Range:
+//    By default, this transform outputs full range code values. If instead a
+//    SMPTE "legal" signal is desired, there is a runtime flag to output
+//    SMPTE legal signal. In ctlrender, this can be achieved by appending
+//    '-param1 legalRange 1' after the '-ctl odt.ctl' string.
+//
+// Assumed observer adapted white point:
+//         CIE 1931 chromaticities:    x            y
+//                                     0.32168      0.33767
+//
+// Viewing Environment:
+//   This ODT has a compensation for viewing environment variables more typical
+//   of those associated with video mastering.
+//
+half3 ODT_Rec709_D60sim_100nits_dim(half3 oces)
+{
+    // OCES to RGB rendering space
+    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
+
+    // Apply the tonescale independently in rendering-space RGB
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
+
+    // Scale luminance to linear code value
+    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
+
+    // --- Compensate for different white point being darker  --- //
+    // This adjustment is to correct an issue that exists in ODTs where the device
+    // is calibrated to a white chromaticity other than D60. In order to simulate
+    // D60 on such devices, unequal code values must be sent to the display to achieve
+    // the chromaticities of D60. More specifically, in order to produce D60 on a device
+    // calibrated to a D65 white point (i.e. equal code values yield CIE x,y
+    // chromaticities of 0.3127, 0.329) the red channel must be slightly higher than
+    // that of green and blue in order to compensate for the relatively more "blue-ish"
+    // D65 white. This unequalness of color channels is the correct behavior but it
+    // means that as neutral highlights increase, the red channel will hit the
+    // device maximum first and clip, resulting in a small chromaticity shift as the
+    // green and blue channels continue to increase to their maximums.
+    // To avoid this clipping error, a slight scale factor is applied to allow the
+    // ODTs to simulate D60 within the D65 calibration white point.
+
+    // Scale and clamp white to avoid casted highlights due to D60 simulation
+    const half SCALE = 0.955;
+    linearCV = min(linearCV, 1.0) * SCALE;
+
+    // Apply gamma adjustment to compensate for dim surround
+    linearCV = darkSurround_to_dimSurround(linearCV);
+
+    // Apply desaturation to compensate for luminance difference
+    //linearCV = mul(ODT_SAT_MAT, linearCV);
+    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
+
+    // Convert to display primary encoding
+    // Rendering space RGB to XYZ
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    // CIE XYZ to display primaries
+    linearCV = mul(XYZ_2_REC709_MAT, XYZ);
+
+    // Handle out-of-gamut values
+    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
+    linearCV = saturate(linearCV);
+
+    // Encode linear code values with transfer function
+    const half DISPGAMMA = 2.4;
+    const half L_W = 1.0;
+    const half L_B = 0.0;
+    half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
+
+    // TODO: Implement support for legal range.
+
+    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
+    // by default which will result in double perceptual encoding, thus for now if one want to use
+    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
+    // compensate for Unity default behaviour.
+
+    return outputCV;
+}
+
+// <ACEStransformID>ODT.Academy.Rec2020_100nits_dim.a1.0.3</ACEStransformID>
+// <ACESuserName>ACES 1.0 Output - Rec.2020</ACESuserName>
+
+//
+// Output Device Transform - Rec2020
+//
+
+//
+// Summary :
+//  This transform is intended for mapping OCES onto a Rec.2020 broadcast
+//  monitor that is calibrated to a D65 white point at 100 cd/m^2. The assumed
+//  observer adapted white is D65, and the viewing environment is that of a dim
+//  surround.
+//
+//  A possible use case for this transform would be UHDTV/video mastering.
+//
+// Device Primaries :
+//  Primaries are those specified in Rec. ITU-R BT.2020
+//  CIE 1931 chromaticities:  x         y         Y
+//              Red:          0.708     0.292
+//              Green:        0.17      0.797
+//              Blue:         0.131     0.046
+//              White:        0.3127    0.329     100 cd/m^2
+//
+// Display EOTF :
+//  The reference electro-optical transfer function specified in
+//  Rec. ITU-R BT.1886.
+//
+// Signal Range:
+//    By default, this transform outputs full range code values. If instead a
+//    SMPTE "legal" signal is desired, there is a runtime flag to output
+//    SMPTE legal signal. In ctlrender, this can be achieved by appending
+//    '-param1 legalRange 1' after the '-ctl odt.ctl' string.
+//
+// Assumed observer adapted white point:
+//         CIE 1931 chromaticities:    x            y
+//                                     0.3127       0.329
+//
+// Viewing Environment:
+//   This ODT has a compensation for viewing environment variables more typical
+//   of those associated with video mastering.
+//
+
+half3 ODT_Rec2020_100nits_dim(half3 oces)
+{
+    // OCES to RGB rendering space
+    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
+
+    // Apply the tonescale independently in rendering-space RGB
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
+
+    // Scale luminance to linear code value
+    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
+
+    // Apply gamma adjustment to compensate for dim surround
+    linearCV = darkSurround_to_dimSurround(linearCV);
+
+    // Apply desaturation to compensate for luminance difference
+    //linearCV = mul(ODT_SAT_MAT, linearCV);
+    linearCV = lerp(dot(linearCV, AP1_RGB2Y).xxx, linearCV, ODT_SAT_FACTOR.xxx);
+
+    // Convert to display primary encoding
+    // Rendering space RGB to XYZ
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    // Apply CAT from ACES white point to assumed observer adapted white point
+    XYZ = mul(D60_2_D65_CAT, XYZ);
+
+    // CIE XYZ to display primaries
+    linearCV = mul(XYZ_2_REC2020_MAT, XYZ);
+
+    // Handle out-of-gamut values
+    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
+    linearCV = saturate(linearCV);
+
+    // Encode linear code values with transfer function
+    const half DISPGAMMA = 2.4;
+    const half L_W = 1.0;
+    const half L_B = 0.0;
+    half3 outputCV = linear_to_bt1886(linearCV, DISPGAMMA, L_W, L_B);
+
+    // TODO: Implement support for legal range.
+
+    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
+    // by default which will result in double perceptual encoding, thus for now if one want to use
+    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
+    // compensate for Unity default behaviour.
+
+    return outputCV;
+}
+
+// <ACEStransformID>ODT.Academy.P3DCI_48nits.a1.0.3</ACEStransformID>
+// <ACESuserName>ACES 1.0 Output - P3-DCI</ACESuserName>
+
+//
+// Output Device Transform - P3DCI (D60 Simulation)
+//
+
+//
+// Summary :
+//  This transform is intended for mapping OCES onto a P3 digital cinema
+//  projector that is calibrated to a DCI white point at 48 cd/m^2. The assumed
+//  observer adapted white is D60, and the viewing environment is that of a dark
+//  theater.
+//
+// Device Primaries :
+//  CIE 1931 chromaticities:  x         y         Y
+//              Red:          0.68      0.32
+//              Green:        0.265     0.69
+//              Blue:         0.15      0.06
+//              White:        0.314     0.351     48 cd/m^2
+//
+// Display EOTF :
+//  Gamma: 2.6
+//
+// Assumed observer adapted white point:
+//         CIE 1931 chromaticities:    x            y
+//                                     0.32168      0.33767
+//
+// Viewing Environment:
+//  Environment specified in SMPTE RP 431-2-2007
+//
+half3 ODT_P3DCI_48nits(half3 oces)
+{
+    // OCES to RGB rendering space
+    half3 rgbPre = mul(AP0_2_AP1_MAT, oces);
+
+    // Apply the tonescale independently in rendering-space RGB
+    half3 rgbPost;
+    rgbPost.x = segmented_spline_c9_fwd(rgbPre.x);
+    rgbPost.y = segmented_spline_c9_fwd(rgbPre.y);
+    rgbPost.z = segmented_spline_c9_fwd(rgbPre.z);
+
+    // Scale luminance to linear code value
+    half3 linearCV = Y_2_linCV(rgbPost, CINEMA_WHITE, CINEMA_BLACK);
+
+    // --- Compensate for different white point being darker  --- //
+    // This adjustment is to correct an issue that exists in ODTs where the device
+    // is calibrated to a white chromaticity other than D60. In order to simulate
+    // D60 on such devices, unequal code values are sent to the display to achieve
+    // neutrals at D60. In order to produce D60 on a device calibrated to the DCI
+    // white point (i.e. equal code values yield CIE x,y chromaticities of 0.314,
+    // 0.351) the red channel is higher than green and blue to compensate for the
+    // "greenish" DCI white. This is the correct behavior but it means that as
+    // highlight increase, the red channel will hit the device maximum first and
+    // clip, resulting in a chromaticity shift as the green and blue channels
+    // continue to increase.
+    // To avoid this clipping error, a slight scale factor is applied to allow the
+    // ODTs to simulate D60 within the D65 calibration white point. However, the
+    // magnitude of the scale factor required for the P3DCI ODT was considered too
+    // large. Therefore, the scale factor was reduced and the additional required
+    // compression was achieved via a reshaping of the highlight rolloff in
+    // conjunction with the scale. The shape of this rolloff was determined
+    // throught subjective experiments and deemed to best reproduce the
+    // "character" of the highlights in the P3D60 ODT.
+
+    // Roll off highlights to avoid need for as much scaling
+    const half NEW_WHT = 0.918;
+    const half ROLL_WIDTH = 0.5;
+    linearCV.x = roll_white_fwd(linearCV.x, NEW_WHT, ROLL_WIDTH);
+    linearCV.y = roll_white_fwd(linearCV.y, NEW_WHT, ROLL_WIDTH);
+    linearCV.z = roll_white_fwd(linearCV.z, NEW_WHT, ROLL_WIDTH);
+
+    // Scale and clamp white to avoid casted highlights due to D60 simulation
+    const half SCALE = 0.96;
+    linearCV = min(linearCV, NEW_WHT) * SCALE;
+
+    // Convert to display primary encoding
+    // Rendering space RGB to XYZ
+    half3 XYZ = mul(AP1_2_XYZ_MAT, linearCV);
+
+    // CIE XYZ to display primaries
+    linearCV = mul(XYZ_2_DCIP3_MAT, XYZ);
+
+    // Handle out-of-gamut values
+    // Clip values < 0 or > 1 (i.e. projecting outside the display primaries)
+    linearCV = saturate(linearCV);
+
+    // Encode linear code values with transfer function
+    const half DISPGAMMA = 2.6;
+    half3 outputCV = pow(linearCV, 1.0 / DISPGAMMA);
+
+    // NOTE: Unity framebuffer encoding is encoded with sRGB opto-electrical transfer function (OETF)
+    // by default which will result in double perceptual encoding, thus for now if one want to use
+    // this ODT, he needs to decode its output with sRGB electro-optical transfer function (EOTF) to
+    // compensate for Unity default behaviour.
+
+    return outputCV;
+}
+
+#endif // __ACES__

--
Gitblit v1.8.0