@@ -84,21 +84,14 @@ vec3 tonemap_aces(vec3 color, float p_white) {
84
84
return color_tonemapped / p_white_tonemapped;
85
85
}
86
86
87
- // Polynomial approximation of EaryChow's AgX sigmoid curve.
88
- // x must be within the range [0.0, 1.0]
89
- vec3 agx_contrast_approx(vec3 x) {
90
- // Generated with Excel trendline
91
- // Input data: Generated using python sigmoid with EaryChow's configuration and 57 steps
92
- // Additional padding values were added to give correct intersections at 0.0 and 1.0
93
- // 6th order, intercept of 0.0 to remove an operation and ensure intersection at 0.0
94
- vec3 x2 = x * x;
95
- vec3 x4 = x2 * x2;
96
- return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2;
97
- }
98
-
99
- // This is an approximation and simplification of EaryChow's AgX implementation that is used by Blender.
87
+ // This is a simplified glsl implementation of EaryChow's AgX that is used by Blender.
88
+ // Input: unbounded linear Rec. 709
89
+ // Output: unbounded linear Rec. 709 (Most any value you care about will be within [0.0, 1.0], thus safe to clip.)
100
90
// This code is based off of the script that generates the AgX_Base_sRGB.cube LUT that Blender uses.
101
91
// Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py
92
+ // Changes: Negative clipping in input color space without "guard rails" and no chroma-angle mixing.
93
+ // Repository for this code: https://github.com/allenwp/AgX-GLSL-Shaders
94
+ // Refer to source repository for other matrices if input/output color space ever changes.
102
95
vec3 tonemap_agx(vec3 color) {
103
96
// Combined linear sRGB to linear Rec 2020 and Blender AgX inset matrices:
104
97
const mat3 srgb_to_rec2020_agx_inset_matrix = mat3 (
@@ -112,11 +105,12 @@ vec3 tonemap_agx(vec3 color) {
112
105
- 0.85585845117807513559 , 1.3264510741502356555 , - 0.23822464068860595117 ,
113
106
- 0.10886710826831608324 , - 0.027084020983874825605 , 1.402665347143271889 );
114
107
115
- // LOG2_MIN = -10.0
116
- // LOG2_MAX = +6.5
117
- // MIDDLE_GRAY = 0.18
118
- const float min_ev = - 12.4739311883324 ; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY)
119
- const float max_ev = 4.02606881166759 ; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY)
108
+ // Terms of Timothy Lottes' tonemapping curve equation:
109
+ // c and d are calculated based on a and d with AgX mid and max parameters.
110
+ const vec3 a = vec3 (1.36989969378897 );
111
+ const float c = 0.3589386656982 ;
112
+ const float b = 1.4325264680543 ;
113
+ const vec3 d = vec3 (0.903916850555009 );
120
114
121
115
// Large negative values in one channel and large positive values in other
122
116
// channels can result in a colour that appears darker and more saturated than
@@ -125,28 +119,16 @@ vec3 tonemap_agx(vec3 color) {
125
119
// This is done before the Rec. 2020 transform to allow the Rec. 2020
126
120
// transform to be combined with the AgX inset matrix. This results in a loss
127
121
// of color information that could be correctly interpreted within the
128
- // Rec. 2020 color space as positive RGB values, but it is less common for Godot
129
- // to provide this function with negative sRGB values and therefore not worth
122
+ // Rec. 2020 color space as positive RGB values, but is often not worth
130
123
// the performance cost of an additional matrix multiplication.
131
- // A value of 2e-10 intentionally introduces insignificant error to prevent
132
- // log2(0.0) after the inset matrix is applied; color will be >= 1e-10 after
133
- // the matrix transform.
134
- color = max (color, 2e-10 );
124
+ color = max (color, 0.0 );
135
125
136
- // Do AGX in rec2020 to match Blender and then apply inset matrix.
126
+ // Apply inset matrix.
137
127
color = srgb_to_rec2020_agx_inset_matrix * color;
138
128
139
- // Log2 space encoding.
140
- // Must be clamped because agx_contrast_approx may not work
141
- // well with values outside of the range [0.0, 1.0]
142
- color = clamp (log2 (color), min_ev, max_ev);
143
- color = (color - min_ev) / (max_ev - min_ev);
144
-
145
- // Apply sigmoid function approximation.
146
- color = agx_contrast_approx(color);
147
-
148
- // Convert back to linear before applying outset matrix.
149
- color = pow (color, vec3 (2.4 ));
129
+ // Use Timothy Lottes' tonemapping equation to approximate AgX's curve.
130
+ color = pow (color, a);
131
+ color = color / (pow (color, d) * b + c);
150
132
151
133
// Apply outset to make the result more chroma-laden and then go back to linear sRGB.
152
134
color = agx_outset_rec2020_to_srgb_matrix * color;
0 commit comments