// urn:ampas:aces:transformId:v1.5:ACESlib.RRT_Common.a1.1.0
// ACES 1.0 Lib - RRT Common
//
// Contains functions and constants shared by forward and inverse RRT transforms
//
// "Glow" module constants
const float RRT_GLOW_GAIN = 0.05;
const float RRT_GLOW_MID = 0.08;
// Red modifier constants
const float RRT_RED_SCALE = 0.82;
const float RRT_RED_PIVOT = 0.03;
const float RRT_RED_HUE = 0.;
const float RRT_RED_WIDTH = 135.;
// Desaturation contants
const float RRT_SAT_FACTOR = 0.96;
const float RRT_SAT_MAT[3][3] = calc_sat_adjust_matrix( RRT_SAT_FACTOR, AP1_RGB2Y);
// ------- Glow module functions
float glow_fwd( float ycIn, float glowGainIn, float glowMid)
{
float glowGainOut;
if (ycIn <= 2./3. * glowMid) {
glowGainOut = glowGainIn;
} else if ( ycIn >= 2. * glowMid) {
glowGainOut = 0.;
} else {
glowGainOut = glowGainIn * (glowMid / ycIn - 1./2.);
}
return glowGainOut;
}
float glow_inv( float ycOut, float glowGainIn, float glowMid)
{
float glowGainOut;
if (ycOut <= ((1 + glowGainIn) * 2./3. * glowMid)) {
glowGainOut = -glowGainIn / (1 + glowGainIn);
} else if ( ycOut >= (2. * glowMid)) {
glowGainOut = 0.;
} else {
glowGainOut = glowGainIn * (glowMid / ycOut - 1./2.) / (glowGainIn / 2. - 1.);
}
return glowGainOut;
}
float sigmoid_shaper( float x)
{
// Sigmoid function in the range 0 to 1 spanning -2 to +2.
float t = max( 1. - fabs( x / 2.), 0.);
float y = 1. + sign(x) * (1. - t * t);
return y / 2.;
}
// ------- Red modifier functions
float cubic_basis_shaper
(
varying float x,
varying float w // full base width of the shaper function (in degrees)
)
{
float M[4][4] = { { -1./6, 3./6, -3./6, 1./6 },
{ 3./6, -6./6, 3./6, 0./6 },
{ -3./6, 0./6, 3./6, 0./6 },
{ 1./6, 4./6, 1./6, 0./6 } };
float knots[5] = { -w/2.,
-w/4.,
0.,
w/4.,
w/2. };
float y = 0;
if ((x > knots[0]) && (x < knots[4])) {
float knot_coord = (x - knots[0]) * 4./w;
int j = knot_coord;
float t = knot_coord - j;
float monomials[4] = { t*t*t, t*t, t, 1. };
// (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/2.;
}
float center_hue( float hue, float centerH)
{
float hueCentered = hue - centerH;
if (hueCentered < -180.) hueCentered = hueCentered + 360.;
else if (hueCentered > 180.) hueCentered = hueCentered - 360.;
return hueCentered;
}
float uncenter_hue( float hueCentered, float centerH)
{
float hue = hueCentered + centerH;
if (hue < 0.) hue = hue + 360.;
else if (hue > 360.) hue = hue - 360.;
return hue;
}
float[3] rrt_sweeteners( float in[3])
{
float aces[3] = in;
// --- Glow module --- //
float saturation = rgb_2_saturation( aces);
float ycIn = rgb_2_yc( aces);
float s = sigmoid_shaper( (saturation - 0.4) / 0.2);
float addedGlow = 1. + glow_fwd( ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
aces = mult_f_f3( addedGlow, aces);
// --- Red modifier --- //
float hue = rgb_2_hue( aces);
float centeredHue = center_hue( hue, RRT_RED_HUE);
float hueWeight = cubic_basis_shaper( centeredHue, RRT_RED_WIDTH);
aces[0] = aces[0] + hueWeight * saturation * (RRT_RED_PIVOT - aces[0]) * (1. - RRT_RED_SCALE);
// --- ACES to RGB rendering space --- //
aces = clamp_f3( aces, 0., HALF_POS_INF);
float rgbPre[3] = mult_f3_f44( aces, AP0_2_AP1_MAT);
rgbPre = clamp_f3( rgbPre, 0., HALF_MAX);
// --- Global desaturation --- //
rgbPre = mult_f3_f33( rgbPre, RRT_SAT_MAT);
return rgbPre;
}
float[3] inv_rrt_sweeteners( float in[3])
{
float rgbPost[3] = in;
// --- Global desaturation --- //
rgbPost = mult_f3_f33( rgbPost, invert_f33(RRT_SAT_MAT));
rgbPost = clamp_f3( rgbPost, 0., HALF_MAX);
// --- RGB rendering space to ACES --- //
float aces[3] = mult_f3_f44( rgbPost, AP1_2_AP0_MAT);
aces = clamp_f3( aces, 0., HALF_MAX);
// --- Red modifier --- //
float hue = rgb_2_hue( aces);
float centeredHue = center_hue( hue, RRT_RED_HUE);
float hueWeight = cubic_basis_shaper( centeredHue, RRT_RED_WIDTH);
float minChan;
if (centeredHue < 0) { // min_f3(aces) = aces[1] (i.e. magenta-red)
minChan = aces[1];
} else { // min_f3(aces) = aces[2] (i.e. yellow-red)
minChan = aces[2];
}
float a = hueWeight * (1. - RRT_RED_SCALE) - 1.;
float b = aces[0] - hueWeight * (RRT_RED_PIVOT + minChan) * (1. - RRT_RED_SCALE);
float c = hueWeight * RRT_RED_PIVOT * minChan * (1. - RRT_RED_SCALE);
aces[0] = ( -b - sqrt( b * b - 4. * a * c)) / ( 2. * a);
// --- Glow module --- //
float saturation = rgb_2_saturation( aces);
float ycOut = rgb_2_yc( aces);
float s = sigmoid_shaper( (saturation - 0.4) / 0.2);
float reducedGlow = 1. + glow_inv( ycOut, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
aces = mult_f_f3( ( reducedGlow), aces);
return aces;
}