Skip to content

Commit ff7cc3b

Browse files
committed
Add AGX tone-mapping function
With "punchy" variant as well.
1 parent a60fdc4 commit ff7cc3b

File tree

3 files changed

+53
-11
lines changed

3 files changed

+53
-11
lines changed

docs/shaders.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,12 @@ image (luminance > $0.5$).
361361
adjustments, Values less than 1 will lighten the output image and values
362362
greater than 1 will darken it.
363363

364-
`tonemap=` [ `:reinhard` | `:aces` ]
364+
`tonemap=` [ `:reinhard` | `:aces` | `:agx` | `:agx_punchy` ]
365365
: If specified, then a tone-mapping function will be applied to map high
366366
dynamic range images into the $[0,1]$ range. The supported tone-mapping
367-
functions are: the Reinhard curve function, and a (close approximation of) the
368-
ACES filmic function. Default is no tone-mapping.
367+
functions are: the Reinhard curve function, a (close approximation of) the
368+
ACES filmic function and a (close approximation) of the AGX and AGX "Punchy"
369+
tone-mapping functions. Default is no tone-mapping.
369370

370371
If `tonemap=:reinhard` then an additional attribute is supported:
371372

src/flitter/render/window/glsl/color_functions.glsl

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,64 @@ float srgb_luminance(vec3 c) {
88
return dot(c, vec3(0.2126, 0.7152, 0.0722));
99
}
1010

11-
vec3 tonemap_reinhard(vec3 c, float whitepoint) {
12-
vec3 cd = c / (1.0 + c);
11+
vec3 tonemap_reinhard(vec3 color, float whitepoint) {
12+
vec3 cd = color / (1.0 + color);
1313
if (whitepoint > 1.0) {
14-
cd *= (1.0 + c / (whitepoint * whitepoint));
14+
cd *= (1.0 + color / (whitepoint * whitepoint));
1515
}
1616
return cd;
1717
}
1818

19+
// This function based on https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
20+
//
1921
vec3 tonemap_aces(vec3 color)
2022
{
21-
// This function based on https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
22-
//
2323
color = mat3(0.59719, 0.076, 0.0284, 0.35458, 0.90834, 0.13383, 0.04823, 0.01566, 0.83777) * color;
2424
color = (color * (color + 0.0245786) - 0.000090537) / (color * (0.983729 * color + 0.4329510) + 0.238081);
2525
return mat3(1.60475, -0.10208, -0.00327, -0.53108, 1.10813, -0.07276, -0.07367, -0.00605, 1.07602) * color;
2626
}
2727

28+
// These functions based on https://iolite-engine.com/blog_posts/minimal_agx_implementation
29+
//
30+
vec3 agx_default_contrast_approx(vec3 x) {
31+
vec3 x2 = x * x;
32+
vec3 x4 = x2 * x2;
33+
return 15.5*x4*x2 - 40.14*x4*x + 31.96*x4 - 6.868*x2*x + 0.4298*x2 + 0.1191*x - 0.00232;
34+
}
35+
36+
vec3 agx(vec3 color) {
37+
const mat3 agx_mat = mat3(0.842479062253094, 0.0423282422610123, 0.0423756549057051,
38+
0.0784335999999992, 0.878468636469772, 0.0784336,
39+
0.0792237451477643, 0.0791661274605434, 0.879142973793104);
40+
const float min_ev = -12.47393;
41+
const float max_ev = 4.026069;
42+
color = agx_mat * color;
43+
color = (clamp(log2(color), min_ev, max_ev) - min_ev) / (max_ev - min_ev);
44+
return agx_default_contrast_approx(color);
45+
}
46+
47+
vec3 agx_eotf(vec3 color) {
48+
const mat3 agx_mat_inv = mat3(1.19687900512017, -0.0528968517574562, -0.0529716355144438,
49+
-0.0980208811401368, 1.15190312990417, -0.0980434501171241,
50+
-0.0990297440797205, -0.0989611768448433, 1.15107367264116);
51+
return agx_mat_inv * color;
52+
}
53+
54+
vec3 tonemap_agx(vec3 color)
55+
{
56+
color = agx(color);
57+
return agx_eotf(color);
58+
}
59+
60+
vec3 tonemap_agx_punchy(vec3 color)
61+
{
62+
color = agx(color);
63+
float luma = srgb_luminance(color);
64+
color = pow(color, vec3(1.35));
65+
color = luma + 1.4 * (color - luma);
66+
return agx_eotf(color);
67+
}
68+
2869
vec3 rgb_to_hsv(vec3 color) {
2970
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
3071
vec4 p = mix(vec4(color.bg, K.wz), vec4(color.gb, K.xy), step(color.b, color.g));
@@ -35,7 +76,7 @@ vec3 rgb_to_hsv(vec3 color) {
3576
}
3677

3778
vec3 hsv_to_rgb(vec3 color) {
38-
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
79+
const vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
3980
vec3 p = abs(fract(color.xxx + K.xyz) * 6.0 - K.www);
4081
return color.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), color.y);
4182
}

src/flitter/render/window/shaders.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ def render(self, node, references, **kwargs):
4343

4444
class Adjust(Shader):
4545
DEFAULT_FRAGMENT_SOURCE = TemplateLoader.get_template('adjust.frag')
46-
TONEMAP_FUNCTIONS = {'reinhard', 'aces'}
46+
TONEMAP_FUNCTIONS = {'reinhard', 'aces', 'agx', 'agx_punchy'}
4747

4848
def render(self, node, references, **kwargs):
4949
tonemap = node.get('tonemap', 1, str)
5050
if tonemap not in self.TONEMAP_FUNCTIONS:
5151
tonemap = None
5252
super().render(node, references, exposure=0, contrast=1, brightness=0, shadows=0, highlights=0,
5353
hue=0, saturation=1, color_matrix=(1, 0, 0, 0, 1, 0, 0, 0, 1),
54-
gamma=1, tonemap_function=tonemap, **kwargs)
54+
gamma=1, tonemap_function=tonemap, whitepoint=0, **kwargs)
5555

5656

5757
class Blur(Shader):

0 commit comments

Comments
 (0)