-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathGamut_Compression.dctl
132 lines (118 loc) · 5.45 KB
/
Gamut_Compression.dctl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Gamut compression experiments, by Jed Smith
// https://gist.github.com/jedypod/ea25c5ff2eed68bfeaaafddd26958133
// Converted to DCTL by Nick Shaw, www.antlerpost.com
DEFINE_UI_PARAMS(threshold, Threshold, DCTLUI_SLIDER_FLOAT, 0.2f, 0.0f, 0.3f, 0.0f);
DEFINE_UI_PARAMS(cyan, Cyan, DCTLUI_SLIDER_FLOAT, 1.0f, 0.0f, 1.2f, 0.0);
DEFINE_UI_PARAMS(magenta, Magenta, DCTLUI_SLIDER_FLOAT, 1.0f, 0.0f, 1.2f, 0.0);
DEFINE_UI_PARAMS(yellow, Yellow, DCTLUI_SLIDER_FLOAT, 1.0f, 0.0f, 1.2f, 0.0);
DEFINE_UI_PARAMS(method, Compression, DCTLUI_COMBO_BOX, 0, {T, E, S}, {tanh, exp, simple});
DEFINE_UI_PARAMS(workingSpace, Working Space, DCTLUI_COMBO_BOX, 0, {ACEScct, ACEScg}, {ACEScct, ACEScg});
DEFINE_UI_PARAMS(invert, Invert, DCTLUI_CHECK_BOX, 0);
// Convert ACEScg to ACEScct
__DEVICE__ float lin_to_ACEScct(float in)
{
if (in <= 0.0078125f)
{
return 10.5402377416545f * in + 0.0729055341958355f;
}
else
{
return (_log2f(in) + 9.72f) / 17.52f;
}
}
// Convert ACEScct to ACEScg
__DEVICE__ float ACEScct_to_lin(float in)
{
if (in > 0.155251141552511f)
{
return _powf( 2.0f, in*17.52f - 9.72f);
}
else
{
return (in - 0.0729055341958355f) / 10.5402377416545f;
}
}
__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B)
{
float3 result;
float r = p_R;
float g = p_G;
float b = p_B;
if (workingSpace == ACEScct) {
// linearise ACEScct
r = ACEScct_to_lin(r);
g = ACEScct_to_lin(g);
b = ACEScct_to_lin(b);
}
// thr is the complement of threshold.
// that is: the percentage of the core gamut to protect
float thr = 1.0f - threshold;
// bias limits by color component
// range is limited to 0.00001 > lim < 1/thr
// cyan = 0: no compression
// cyan = 1: "normal" compression with limit at 1.0
// 1 > cyan < 1/thr : compress more than edge of gamut. max = hard clip (e.g., thr=0.8, max = 1.25)
float3 lim;
lim.x = 1.0f/max(0.00001f, min(1.0f/thr, cyan));
lim.y = 1.0f/max(0.00001f, min(1.0f/thr, magenta));
lim.z = 1.0f/max(0.00001f, min(1.0f/thr, yellow));
// achromatic axis
float ach = _fmaxf(r, _fmaxf(g, b));
// distance from the achromatic axis for each color component
float d_r = ach == 0.0f ? 0.0f : _fabs(r-ach) / ach;
float d_g = ach == 0.0f ? 0.0f : _fabs(g-ach) / ach;
float d_b = ach == 0.0f ? 0.0f : _fabs(b-ach) / ach;
// compress distance for each color component
// method 0 : tanh - hyperbolic tangent compression method suggested by Thomas Mansencal https://community.acescentral.com/t/simplistic-gamut-mapping-approaches-in-nuke/2679/2
// method 1 : exp - natural exponent compression method
// method 2 : simple - simple Reinhard type compression suggested by Nick Shaw https://community.acescentral.com/t/simplistic-gamut-mapping-approaches-in-nuke/2679/3
// example plots for each method: https://www.desmos.com/calculator/x69iyptspq
float cd_r, cd_g, cd_b;
if (method == T) {
if (invert == 0) {
cd_r = d_r > thr ? thr + (lim.x - thr) * _tanhf( ( (d_r - thr)/( lim.x-thr))) : d_r;
cd_g = d_g > thr ? thr + (lim.y - thr) * _tanhf( ( (d_g - thr)/( lim.y-thr))) : d_g;
cd_b = d_b > thr ? thr + (lim.z - thr) * _tanhf( ( (d_b - thr)/( lim.z-thr))) : d_b;
} else {
cd_r = d_r > thr ? thr + (lim.x - thr) * _atanhf( d_r/( lim.x - thr) - thr/( lim.x - thr)) : d_r;
cd_g = d_g > thr ? thr + (lim.y - thr) * _atanhf( d_g/( lim.y - thr) - thr/( lim.y - thr)) : d_g;
cd_b = d_b > thr ? thr + (lim.z - thr) * _atanhf( d_b/( lim.z - thr) - thr/( lim.z - thr)) : d_b;
}
} else if (method == E) {
if (invert == 0) {
cd_r = d_r > thr ? lim.x-(lim.x-thr)*exp(-(((d_r-thr)*((1.0f*lim.x)/(lim.x-thr))/lim.x))) : d_r;
cd_g = d_g > thr ? lim.y-(lim.y-thr)*exp(-(((d_g-thr)*((1.0f*lim.y)/(lim.y-thr))/lim.y))) : d_g;
cd_b = d_b > thr ? lim.z-(lim.z-thr)*exp(-(((d_b-thr)*((1.0f*lim.z)/(lim.z-thr))/lim.z))) : d_b;
} else {
cd_r = d_r > thr ? -_logf( (d_r-lim.x)/(thr-lim.x))*(-thr+lim.x)/1.0f+thr : d_r;
cd_g = d_g > thr ? -_logf( (d_g-lim.y)/(thr-lim.y))*(-thr+lim.y)/1.0f+thr : d_g;
cd_b = d_b > thr ? -_logf( (d_b-lim.z)/(thr-lim.z))*(-thr+lim.z)/1.0f+thr : d_b;
}
} else if (method == S) {
if (invert == 0) {
cd_r = d_r > thr ? thr+(-1/((d_r-thr)/(lim.x-thr)+1)+1)*(lim.x-thr) : d_r;
cd_g = d_g > thr ? thr+(-1/((d_g-thr)/(lim.y-thr)+1)+1)*(lim.y-thr) : d_g;
cd_b = d_b > thr ? thr+(-1/((d_b-thr)/(lim.z-thr)+1)+1)*(lim.z-thr) : d_b;
} else {
cd_r = d_r > thr ? (_powf(thr, 2.0f) - thr*d_r + (lim.x-thr)*d_r) / (thr + (lim.x-thr) - d_r) : d_r;
cd_g = d_g > thr ? (_powf(thr, 2.0f) - thr*d_g + (lim.y-thr)*d_g) / (thr + (lim.y-thr) - d_g) : d_g;
cd_b = d_b > thr ? (_powf(thr, 2.0f) - thr*d_b + (lim.z-thr)*d_b) / (thr + (lim.z-thr) - d_b) : d_b;
}
}
// scale each color component relative to achromatic axis by gamut compression factor
float c_r, c_g, c_b;
c_r = ach-cd_r*ach;
c_g = ach-cd_g*ach;
c_b = ach-cd_b*ach;
if (threshold < 0.001f) {
result = make_float3(r, g, b);
} else {
result = make_float3(c_r, c_g, c_b);
}
if (workingSpace == ACEScct) {
result.x = lin_to_ACEScct(result.x);
result.y = lin_to_ACEScct(result.y);
result.z = lin_to_ACEScct(result.z);
}
return result;
}