Update mpv config:

This commit is contained in:
aria 2025-05-26 00:03:04 +10:00
parent 64e685695e
commit d1e0b97d62
Signed by: aria
SSH key fingerprint: SHA256:WqtcVnDMrv1lnUlNah5k31iywFUI/DV+5yHzCTO4Vds
36 changed files with 5555 additions and 10 deletions

59
mpv/hdr-toys.conf Normal file
View file

@ -0,0 +1,59 @@
target-colorspace-hint=no
tone-mapping=clip
gamut-mapping-mode=clip
[bt.2100-pq]
profile-cond=get("video-params/primaries") == "bt.2020" and get("video-params/gamma") == "pq"
profile-restore=copy
target-prim=bt.2020
target-trc=pq
glsl-shader=~~/shaders/hdr-toys/utils/clip_both.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/pq_inv.glsl
glsl-shader=~~/shaders/hdr-toys/tone-mapping/astra.glsl
glsl-shader=~~/shaders/hdr-toys/gamut-mapping/jedypod.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/bt1886.glsl
# glsl-shader-opts=reference_white=100
[bt.2100-hlg]
profile-cond=get("video-params/primaries") == "bt.2020" and get("video-params/gamma") == "hlg"
profile-restore=copy
target-prim=bt.2020
target-trc=hlg
glsl-shader=~~/shaders/hdr-toys/utils/clip_both.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/hlg_inv.glsl
glsl-shader=~~/shaders/hdr-toys/tone-mapping/astra.glsl
glsl-shader=~~/shaders/hdr-toys/gamut-mapping/jedypod.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/bt1886.glsl
[bt.2020]
profile-cond=get("video-params/primaries") == "bt.2020" and get("video-params/gamma") == "bt.1886"
profile-restore=copy
target-prim=bt.2020
target-trc=bt.1886
glsl-shader=~~/shaders/hdr-toys/transfer-function/bt1886_inv.glsl
glsl-shader=~~/shaders/hdr-toys/gamut-mapping/bottosson.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/bt1886.glsl
[openexr]
profile-cond=get("file-format") == "exr_pipe"
profile-restore=copy
target-prim=bt.2020
target-trc=linear
scale=bilinear
glsl-shader=~~/shaders/hdr-toys/utils/clip_black.glsl
glsl-shader=~~/shaders/hdr-toys/tone-mapping/astra.glsl
glsl-shader=~~/shaders/hdr-toys/gamut-mapping/jedypod.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/bt1886.glsl
[radiance]
profile-cond=get("file-format") == "hdr_pipe"
profile-restore=copy
target-prim=bt.2020
target-trc=linear
vf=format:gamma=linear
scale=bilinear
glsl-shader=~~/shaders/hdr-toys/utils/clip_black.glsl
glsl-shader=~~/shaders/hdr-toys/tone-mapping/astra.glsl
glsl-shader=~~/shaders/hdr-toys/gamut-mapping/jedypod.glsl
glsl-shader=~~/shaders/hdr-toys/transfer-function/bt1886.glsl

View file

@ -25,6 +25,12 @@ alt+i script-binding uosc/keybinds #! Utils > Key bindings
O script-binding uosc/show-in-directory #! Utils > Show in directory O script-binding uosc/show-in-directory #! Utils > Show in directory
# script-binding uosc/open-config-directory #! Utils > Open config directory # script-binding uosc/open-config-directory #! Utils > Open config directory
# script-binding uosc/update #! Utils > Update uosc # script-binding uosc/update #! Utils > Update uosc
shift+h apply-profile HDR_MODE:SDR_HDR_EFFECT #! HDR > inverse-tone-mapping
ctrl+shift+h apply-profile HDR_MODE:SDR_HDR_EFFECT restore #! HDR > Undo inverse-tone-mapping
# apply-profile HDR_MODE:SDR #! HDR > SDR under HDR
# apply-profile HDR_MODE:SDR restore #! HDR > Revert SDR under HDR
# apply-profile HDR_MODE:DOVI #! HDR > Dolby Vision
# apply-profile HDR_MODE:DOVI restore #! HDR > Dolby Vision
CTRL+1 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode A (HQ)" CTRL+1 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode A (HQ)"
CTRL+2 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode B (HQ)" CTRL+2 no-osd change-list glsl-shaders set "~~/shaders/Anime4K_Clamp_Highlights.glsl:~~/shaders/Anime4K_Restore_CNN_Soft_VL.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_VL.glsl:~~/shaders/Anime4K_AutoDownscalePre_x2.glsl:~~/shaders/Anime4K_AutoDownscalePre_x4.glsl:~~/shaders/Anime4K_Upscale_CNN_x2_M.glsl"; show-text "Anime4K: Mode B (HQ)"

View file

@ -115,17 +115,38 @@ target-colorspace-hint=auto
# target-peak=auto # target-peak=auto
# gamut-mapping-mode=perceptual # gamut-mapping-mode=perceptual
# hdr-contrast-recovery=1.0 # hdr-contrast-recovery=1.0
#
# [HDR_MODE:DOVI] [HDR_MODE:DOVI]
# profile-restore=copy profile-restore=copy
# #Automatically enables profile if DoVi video is played. Seems to work but maybe there a better way? #Automatically enables profile if DoVi video is played. Seems to work but maybe there a better way?
# profile-cond=(p["video-dec-params/gamma"] == "auto") # profile-cond=(p["video-dec-params/gamma"] == "auto")
# target-trc=pq target-trc=pq
# target-prim=bt.2020 target-prim=bt.2020
# #Adjust this to the peak brightness of your display. e.g. 800 for LG CX #Adjust this to the peak brightness of your display. e.g. 800 for LG CX
# target-peak=1500 target-peak=1037
# #Only necessary if you are switching between different profiles like me SDR_TO_HDR tonemap profile #Only necessary if you are switching between different profiles like me SDR_TO_HDR tonemap profile
# tone-mapping-mode=auto tone-mapping-mode=auto
[HDR_MODE:SDR]
profile-restore=copy
target-trc=pq
target-prim=bt.2020
#Seems to be some kind of magic number, higher values do not have any effect
target-peak=200
tone-mapping=bt.2390
tone-mapping-mode=rgb
inverse-tone-mapping=yes
[HDR_MODE:SDR_HDR_EFFECT]
profile-restore=copy
target-trc=pq
target-prim=bt.2020
# Higher value = stronger effect
target-peak=400
tone-mapping=spline
# All other values make the colors look awful in my opinion.
tone-mapping-mode=rgb
inverse-tone-mapping=yes
################################### ###################################
# Protocol Specific Configuration # # Protocol Specific Configuration #

13
mpv/scripts/hdr-toys.lua Normal file
View file

@ -0,0 +1,13 @@
local options = require("mp.options")
local o = {
temporal_stable_time = 1 / 3,
}
options.read_options(o, _, function() end)
mp.observe_property("container-fps", "native", function (property, value)
if not value then return end
value = value * o.temporal_stable_time
value = math.floor(value + 0.5)
mp.command("no-osd set glsl-shader-opts temporal_stable_frames=" .. value)
end)

View file

@ -0,0 +1,350 @@
// https://bottosson.github.io/posts/gamutclipping/
// https://www.shadertoy.com/view/7sXcWn
//!PARAM softness_scale
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.3
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (bottosson, soft)
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
vec3 cbrt(vec3 color) {
return vec3(
cbrt(color.x),
cbrt(color.y),
cbrt(color.z)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.8190224379967030, 0.3619062600528904, -0.1288737815209879,
0.0329836539323885, 0.9292868615863434, 0.0361446663506424,
0.0481771893596242, 0.2642395317527308, 0.6335478284694309
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.2268798758459243, -0.5578149944602171, 0.2813910456659647,
-0.0405757452148008, 1.1122868032803170, -0.0717110580655164,
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816
);
}
vec3 LMS_to_Lab(vec3 LMS) {
return LMS * mat3(
0.2104542683093140, 0.7936177747023054, -0.0040720430116193,
1.9779985324311684, -2.4285922420485799, 0.4505937096174110,
0.0259040424655478, 0.7827717124575296, -0.8086757549230774
);
}
vec3 Lab_to_LMS(vec3 Lab) {
return Lab * mat3(
1.0000000000000000, 0.3963377773761749, 0.2158037573099136,
1.0000000000000000, -0.1055613458156586, -0.0638541728258133,
1.0000000000000000, -0.0894841775298119, -1.2914855480194092
);
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = cbrt(color);
color = LMS_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_LMS(color);
color = pow(color, vec3(3.0));
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
const float epsilon = 1e-6;
vec3 Lab_to_LCh(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float C = length(vec2(a, b));
float h = (abs(a) < epsilon && abs(b) < epsilon) ? 0.0 : atan(b, a);
return vec3(L, C, h);
}
vec3 LCh_to_Lab(vec3 LCh) {
float L = LCh.x;
float C = LCh.y;
float h = LCh.z;
C = max(C, 0.0);
float a = C * cos(h);
float b = C * sin(h);
return vec3(L, a, b);
}
vec3 output_RGB_to_XYZ(vec3 RGB) {
mat3 M = mat3(
0.41239079926595934, 0.357584339383878, 0.1804807884018343,
0.21263900587151027, 0.715168678767756, 0.07219231536073371,
0.01933081871559182, 0.11919477979462598, 0.9505321522496607);
return RGB * M;
}
vec3 output_XYZ_to_RGB(vec3 XYZ) {
mat3 M = mat3(
3.2409699419045226, -1.537383177570094, -0.4986107602930034,
-0.9692436362808796, 1.8759675015077202, 0.04155505740717559,
0.05563007969699366, -0.20397695888897652, 1.0569715142428786);
return XYZ * M;
}
float findCenter(vec3 x) {
float a = 1.9779985324311684 * x.x - 2.4285922420485799 * x.y + 0.4505937096174110 * x.z;
float b = 0.0259040424655478 * x.x + 0.7827717124575296 * x.y - 0.8086757549230774 * x.z;
float C = sqrt(a*a+b*b);
// Matrix derived for max(l,m,s) to be as close to macadam limit as possible
// this makes it some kind of g0-like estimate
mat3 M = mat3(
2.26923008, -1.43594808, 0.166718,
-0.98545265, 2.12616699, -0.14071434,
-0.02985871, -0.25753239, 1.2873911);
x = x*M;
float x_min = min(x.r,min(x.g,x.b));
float x_max = max(x.r,max(x.g,x.b));
float c = 0.5*(x_max+x_min);
float s = (x_max-x_min);
// math trickery to create values close to c and s, but without producing hard edges
vec3 y = (x-c)/s;
float c_smooth = c + dot(y*y*y, vec3(1.0/3.0))*s;
float s_smooth = sqrt(dot(x-c,x-c)/2.0);
return c_smooth;
}
vec2 findCenterAndPurity(vec3 x) {
// Matrix derived for (c_smooth+s_smooth) to be an approximation of the macadam limit
// this makes it some kind of g0-like estimate
mat3 M = mat3(
2.26775149, -1.43293879, 0.1651873,
-0.98535505, 2.1260072, -0.14065215,
-0.02501605, -0.26349465, 1.2885107);
x = x*M;
float x_min = min(x.r,min(x.g,x.b));
float x_max = max(x.r,max(x.g,x.b));
float c = 0.5*(x_max+x_min);
float s = (x_max-x_min);
// math trickery to create values close to c and s, but without producing hard edges
vec3 y = (x-c)/s;
float c_smooth = c + dot(y*y*y, vec3(1.0/3.0))*s;
float s_smooth = sqrt(dot(x-c,x-c)/2.0);
return vec2(c_smooth, s_smooth);
}
vec3 toLms(vec3 c) {
vec3 lms_ = XYZ_to_LMS(output_RGB_to_XYZ(c));
return sign(lms_)*pow(abs(lms_), vec3(1.0/3.0));
}
float calculateC(vec3 lms) {
// Most of this could be precomputed
// Creating a transform that maps R,G,B in the target gamut to have same distance from grey axis
vec3 lmsR = toLms(vec3(1.0,0.0,0.0));
vec3 lmsG = toLms(vec3(0.0,1.0,0.0));
vec3 lmsB = toLms(vec3(0.0,0.0,1.0));
vec3 uDir = (lmsR - lmsG)/sqrt(2.0);
vec3 vDir = (lmsR + lmsG - 2.0*lmsB)/sqrt(6.0);
mat3 to_uv = inverse(mat3(
1.0, uDir.x, vDir.x,
1.0, uDir.y, vDir.y,
1.0, uDir.z, vDir.z
));
vec3 _uv = lms * to_uv;
return sqrt(_uv.y*_uv.y + _uv.z*_uv.z);
}
vec3 calculateLCh(vec3 c) {
vec3 lms = toLms(c);
float maxLms = findCenter(lms);
float a = 1.9779985324311684 * lms.x - 2.4285922420485799 * lms.y + 0.4505937096174110 * lms.z;
float b = 0.0259040424655478 * lms.x + 0.7827717124575296 * lms.y - 0.8086757549230774 * lms.z;
float C = sqrt(a*a+b*b);
return vec3(maxLms, C, atan(-b, -a));
}
vec2 expandShape(vec3 rgb, vec2 ST) {
vec3 LCh = calculateLCh(rgb);
vec2 STnew = vec2(LCh.x/LCh.y, (1.0-LCh.x)/LCh.y);
STnew = (STnew + 3.0*STnew*STnew*LCh.y);
return vec2(min(ST.x, STnew.x), min(ST.y, STnew.y));
}
float expandScale(vec3 rgb, vec2 ST, float scale) {
vec3 LCh = calculateLCh(rgb);
float Cnew = (1.0/((ST.x/LCh.x) + (ST.y/(1.0-LCh.x))));
return max(LCh.y/Cnew, scale);
}
vec2 approximateShape() {
float m = -softness_scale*0.2;
float s = 1.0 + (softness_scale*0.2+softness_scale*0.8);
vec2 ST = vec2(1000.0,1000.0);
ST = expandShape(m+s*vec3(1.0,0.0,0.0), ST);
ST = expandShape(m+s*vec3(1.0,1.0,0.0), ST);
ST = expandShape(m+s*vec3(0.0,1.0,0.0), ST);
ST = expandShape(m+s*vec3(0.0,1.0,1.0), ST);
ST = expandShape(m+s*vec3(0.0,0.0,1.0), ST);
ST = expandShape(m+s*vec3(1.0,0.0,1.0), ST);
float scale = 0.0;
scale = expandScale(m+s*vec3(1.0,0.0,0.0), ST, scale);
scale = expandScale(m+s*vec3(1.0,1.0,0.0), ST, scale);
scale = expandScale(m+s*vec3(0.0,1.0,0.0), ST, scale);
scale = expandScale(m+s*vec3(0.0,1.0,1.0), ST, scale);
scale = expandScale(m+s*vec3(0.0,0.0,1.0), ST, scale);
scale = expandScale(m+s*vec3(1.0,0.0,1.0), ST, scale);
return ST/scale;
}
vec3 compute(float L, float hue, float sat) {
vec3 c = vec3(L, cos(hue), sin(hue));
float l_ = + 0.3963377773761749 * c.y + 0.2158037573099136 * c.z;
float m_ = - 0.1055613458156586 * c.y - 0.0638541728258133 * c.z;
float s_ = - 0.0894841775298119 * c.y - 1.2914855480194092 * c.z;
vec3 lms = vec3(l_,m_,s_);
vec2 MC = findCenterAndPurity(lms);
lms -= MC.x;
lms *= sat;
lms += c.x;
lms = lms*lms*lms;
vec3 rgb = output_XYZ_to_RGB(LMS_to_XYZ(lms));
return rgb;
}
vec3 softSaturate(vec3 x, vec3 a) {
a = clamp(a, 0.0,softness_scale);
a = 1.0+a;
x = min(x, a);
vec3 b = (a-1.0)*sqrt(a/(2.0-a));
return 1.0 - (sqrt((x-a)*(x-a) + b*b) - b)/(sqrt(a*a+b*b)-b);
}
vec3 softClipColor(vec3 color) {
// soft clip of rgb values to avoid artifacts of hard clipping
// causes hues distortions, but is a smooth mapping
float maxRGB = max(max(color.r, color.g), color.b);
float minRGB = min(min(color.r, color.g), color.b);
float grey = 0.2;
vec3 x = color-grey;
vec3 xsgn = sign(x);
vec3 xscale = 0.5 + xsgn*(0.5-grey);
x /= xscale;
float softness_0 = maxRGB/(1.0+softness_scale)*softness_scale;
float softness_1 = (1.0-minRGB)/(1.0+softness_scale)*softness_scale;
vec3 softness = vec3(0.5)*(softness_0+softness_1 + xsgn*(softness_1 - softness_0));
return grey + xscale*xsgn*softSaturate(abs(x), softness);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
vec3 lch = Lab_to_LCh(RGB_to_Lab(color.rgb));
float L = lch.x;
float C = lch.y;
float h = lch.z;
// if (L >= 1.0) {
// return vec4(vec3(1.0, 1.0, 1.0), color.a);
// }
// if (L <= 0.0) {
// return vec4(vec3(0.0, 0.0, 0.0), color.a);
// }
if (C <= 1e-6) {
return color;
}
vec2 ST = approximateShape();
float C_smooth = (1.0 / ((ST.x / L) + (ST.y / max(1.0 - L, 1e-6))));
color.rgb = compute(L, h, C / sqrt(C * C / C_smooth / C_smooth + 1.0));
color.rgb = softClipColor(color.rgb);
return color;
}

View file

@ -0,0 +1,512 @@
// RGB to RGB conversion, includes chromatic adaptation transform
// All coordinates are based on the CIE 1931 2° chromaticity diagram
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (clip)
// You can use custom chromaticity here.
// Example: BT.709 with a D93 white point: Chromaticity(BT709.r, BT709.g, BT709.b, D93)
// You can also define custom coordinates: Chromaticity(vec2(0.7347, 0.2653), BT709.g, BT709.b, D65)
#define from BT2020
#define to BT709
// White points of standard illuminants
// https://en.wikipedia.org/wiki/Standard_illuminant#White_points_of_standard_illuminants
const vec2 A = vec2(0.44757, 0.40745);
const vec2 B = vec2(0.34842, 0.35161);
const vec2 C = vec2(0.31006, 0.31616);
const vec2 D50 = vec2(0.34567, 0.35850);
const vec2 D55 = vec2(0.33242, 0.34743);
const vec2 D65 = vec2(0.31271, 0.32902);
const vec2 D75 = vec2(0.29902, 0.31485);
const vec2 D93 = vec2(0.28315, 0.29711);
const vec2 E = vec2(1.0/3.0, 1.0/3.0);
const vec2 F2 = vec2(0.37208, 0.37529);
const vec2 F7 = vec2(0.31292, 0.32933);
const vec2 F11 = vec2(0.38052, 0.37713);
const vec2 DCI = vec2(0.31400, 0.35100);
// It is also known as D60
const vec2 ACES = vec2(0.32168, 0.33767);
// Colour Matching Between OLED and CRT
// https://www.sony.jp/products/catalog/FUN_WhitePaper_OLED_ColorMatching_V1_00.pdf
const vec2 BRAVIA = vec2(0.3067, 0.318);
// https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
vec2 CIE_D(float T) {
// Compensate for the loss caused by the accuracy difference between the old and new standards
// c2 = 1.4387768775039337
// https://en.wikipedia.org/wiki/Planckian_locus#Planckian_locus_in_the_XYZ_color_space
T = (T * 1.4388) / 1.438;
// This formula is applicable to temperatures ranging from 4000K to 25000K
T = clamp(T, 4000.0, 25000.0);
float t1 = 1000.0 / T;
float t2 = t1 * t1;
float t3 = t1 * t2;
float x =
T <= 7000.0
? 0.244063 + 0.09911 * t1 + 2.9678 * t2 - 4.607 * t3
: 0.23704 + 0.24748 * t1 + 1.9018 * t2 - 2.0064 * t3;
// Daylight locus
float y = -0.275 + 2.87 * x - 3.0 * x * x;
return vec2(x, y);
}
// https://en.wikipedia.org/wiki/Planckian_locus#Approximation
vec2 Kang(float T) {
// This formula is applicable to temperatures ranging from 1667K to 25000K
T = clamp(T, 1667.0, 25000.0);
float t1 = 1000.0 / T;
float t2 = t1 * t1;
float t3 = t1 * t2;
float x =
T <= 4000.0
? -0.2661239 * t3 - 0.234358 * t2 + 0.8776956 * t1 + 0.17991
: -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t1 + 0.24039;
float x2 = x * x;
float x3 = x2 * x;
float y =
T <= 2222.0
? -1.1063814 * x3 - 1.3481102 * x2 - 2.18555832 * x - 0.20219683
: T <= 4000.0
? -0.9549476 * x3 - 1.37418593 * x2 - 2.09137015 * x - 0.16748867
: 3.081758 * x3 - 5.8733867 * x2 + 3.75112997 * x - 0.37001483;
return vec2(x, y);
}
// Chromaticities
// https://www.itu.int/rec/T-REC-H.273
// https://github.com/colour-science/colour/tree/develop/colour/models/rgb/datasets
struct Chromaticity {
vec2 r, g, b, w;
};
// ITU-R BT.2020, ITU-R BT.2100
const Chromaticity BT2020 = Chromaticity(
vec2(0.708, 0.292),
vec2(0.170, 0.797),
vec2(0.131, 0.046),
D65
);
// ITU-R BT.709, IEC 61966-2-1 (sRGB)
const Chromaticity BT709 = Chromaticity(
vec2(0.64, 0.33),
vec2(0.30, 0.60),
vec2(0.15, 0.06),
D65
);
// ITU-R BT.601 (525 lines), SMPTE ST 240
const Chromaticity BT601_525 = Chromaticity(
vec2(0.630, 0.340),
vec2(0.310, 0.595),
vec2(0.155, 0.070),
D65
);
// ITU-R BT.601 (625 lines), BT.470 (B/G), EBU 3213-E
const Chromaticity BT601_625 = Chromaticity(
vec2(0.64, 0.33),
vec2(0.29, 0.60),
vec2(0.15, 0.06),
D65
);
// ITU-R BT.470 (M)
const Chromaticity BT470m = Chromaticity(
vec2(0.67, 0.33),
vec2(0.21, 0.71),
vec2(0.14, 0.08),
C
);
// P3-DCI (Theater)
const Chromaticity P3DCI = Chromaticity(
vec2(0.680, 0.320),
vec2(0.265, 0.690),
vec2(0.150, 0.060),
DCI
);
// P3-D65 (Display)
const Chromaticity P3D65 = Chromaticity(
P3DCI.r,
P3DCI.g,
P3DCI.b,
D65
);
// P3-D60 (ACES Cinema)
const Chromaticity P3D60 = Chromaticity(
P3DCI.r,
P3DCI.g,
P3DCI.b,
ACES
);
// ITU-T H.273 (Generic film)
const Chromaticity H273_8 = Chromaticity(
vec2(0.681, 0.319),
vec2(0.243, 0.692),
vec2(0.145, 0.049),
C
);
// ITU-T H.273 (No corresponding industry specification identified)
const Chromaticity H273_22 = Chromaticity(
vec2(0.630, 0.340),
vec2(0.295, 0.605),
vec2(0.155, 0.077),
D65
);
// CIE RGB (CIE 1931 color space)
const Chromaticity CIERGB = Chromaticity(
vec2(0.73474284, 0.26525716),
vec2(0.27377903, 0.7174777),
vec2(0.16655563, 0.00891073),
E
);
// CIE XYZ (CIE 1931 color space)
const Chromaticity XYZ = Chromaticity(
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(0.0, 0.0),
E
);
// CIE XYZ (CIE 1931 color space, D65 whitepoint)
const Chromaticity XYZD65 = Chromaticity(
XYZ.r,
XYZ.g,
XYZ.b,
D65
);
// CIE XYZ (CIE 1931 color space, D50 whitepoint)
const Chromaticity XYZD50 = Chromaticity(
XYZ.r,
XYZ.g,
XYZ.b,
D50
);
// Grayscale, Monochrome
const Chromaticity GRAY = Chromaticity(
vec2(0.0, 1.0),
vec2(0.0, 1.0),
vec2(0.0, 1.0),
E
);
// Adobe RGB (1998)
const Chromaticity AdobeRGB = Chromaticity(
vec2(0.64, 0.33),
vec2(0.21, 0.71),
vec2(0.15, 0.06),
D65
);
// Adobe Wide Gamut RGB
const Chromaticity AdobeWideGamutRGB = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.1152, 0.8264),
vec2(0.1566, 0.0177),
D50
);
// ROMM (ProPhoto RGB)
const Chromaticity ROMM = Chromaticity(
vec2(0.734699, 0.265301),
vec2(0.159597, 0.840403),
vec2(0.036598, 0.000105),
D50
);
// AP0 (ACES 2065-1)
const Chromaticity AP0 = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.0000, 1.0000),
vec2(0.0001, -0.0770),
ACES
);
// AP1 (ACEScg, cc, cct, proxy)
const Chromaticity AP1 = Chromaticity(
vec2(0.713, 0.293),
vec2(0.165, 0.830),
vec2(0.128, 0.044),
ACES
);
// ARRI Wide Gamut 3
const Chromaticity AWG3 = Chromaticity(
vec2(0.684, 0.313),
vec2(0.221, 0.848),
vec2(0.0861, -0.102),
D65
);
// ARRI Wide Gamut 4
const Chromaticity AWG4 = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.1424, 0.8576),
vec2(0.0991, -0.0308),
D65
);
// RED Wide Gamut RGB
const Chromaticity RWG = Chromaticity(
vec2(0.780308, 0.304253),
vec2(0.121595, 1.493994),
vec2(0.095612, -0.084589),
D65
);
// DaVinci Wide Gamut
const Chromaticity DWG = Chromaticity(
vec2(0.8000, 0.3130),
vec2(0.1682, 0.9877),
vec2(0.0790, -0.1155),
D65
);
// FilmLight E-Gamut
const Chromaticity EGAMUT = Chromaticity(
vec2(0.8000, 0.3177),
vec2(0.1800, 0.9000),
vec2(0.0650, -0.0805),
D65
);
// FilmLight E-Gamut 2
const Chromaticity EGAMUT2 = Chromaticity(
vec2(0.8300, 0.3100),
vec2(0.1500, 0.9500),
vec2(0.0650, -0.0805),
D65
);
// FUJIFILM F-Gamut C
const Chromaticity FGAMUTC = Chromaticity(
vec2(0.7347, 0.2653),
vec2(0.0263, 0.9737),
vec2(0.1173, -0.0224),
D65
);
// Sony S-Gamut3/S-Gamut
const Chromaticity SGAMUT = Chromaticity(
vec2(0.73, 0.280),
vec2(0.14, 0.855),
vec2(0.10, -0.050),
D65
);
// Sony S-Gamut.Cine
const Chromaticity SGAMUTCINE = Chromaticity(
vec2(0.766, 0.275),
vec2(0.225, 0.800),
vec2(0.089, -0.087),
D65
);
// Canon Cinema Gamut
const Chromaticity CINEMA_GAMUT = Chromaticity(
vec2(0.74, 0.27),
vec2(0.17, 1.14),
vec2(0.08, -0.10),
D65
);
// Panasonic V-Gamut
const Chromaticity VGAMUT = Chromaticity(
vec2(0.730, 0.28),
vec2(0.165, 0.84),
vec2(0.100, -0.03),
D65
);
// DJI D-Gamut
const Chromaticity DGAMUT = Chromaticity(
vec2(0.71, 0.31),
vec2(0.21, 0.88),
vec2(0.09, -0.08),
D65
);
// Chromatic adaptation transform
// https://en.wikipedia.org/wiki/LMS_color_space
// It is also known as von Kries
const mat3 HPE = mat3(
0.40024, 0.70760, -0.08081,
-0.22630, 1.16532, 0.04570,
0.00000, 0.00000, 0.91822
);
const mat3 Bradford = mat3(
0.8951, 0.2664, -0.1614,
-0.7502, 1.7135, 0.0367,
0.0389, -0.0685, 1.0296
);
const mat3 CAT97 = mat3(
0.8562, 0.3372, -0.1934,
-0.8360, 1.8327, 0.0033,
0.0357, -0.0469, 1.0112
);
const mat3 CAT02 = mat3(
0.7328, 0.4296, -0.1624,
-0.7036, 1.6975, 0.0061,
0.0030, 0.0136, 0.9834
);
const mat3 CAT16 = mat3(
0.401288, 0.650173, -0.051461,
-0.250268, 1.204414, 0.045854,
-0.002079, 0.048952, 0.953127
);
// Other constants
const mat3 Identity3 = mat3(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
const mat3 SingularY3 = mat3(
0.0, 1.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 0.0
);
// http://www.brucelindbloom.com/Eqn_xyY_to_XYZ.html
vec3 xyY_to_XYZ(vec3 xyY) {
float x = xyY.x;
float y = xyY.y;
float Y = xyY.z;
float multiplier = Y / max(y, 1e-6);
float z = 1.0 - x - y;
float X = x * multiplier;
float Z = z * multiplier;
return vec3(X, Y, Z);
}
// http://www.brucelindbloom.com/Eqn_XYZ_to_xyY.html
vec3 XYZ_to_xyY(vec3 XYZ) {
float X = XYZ.x;
float Y = XYZ.y;
float Z = XYZ.z;
float divisor = X + Y + Z;
if (divisor == 0.0) divisor = 1e-6;
float x = X / divisor;
float y = Y / divisor;
return vec3(x, y, Y);
}
// http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
mat3 RGB_to_XYZ(Chromaticity C) {
if (C == GRAY)
return Identity3;
vec3 r = xyY_to_XYZ(vec3(C.r, 1.0));
vec3 g = xyY_to_XYZ(vec3(C.g, 1.0));
vec3 b = xyY_to_XYZ(vec3(C.b, 1.0));
vec3 w = xyY_to_XYZ(vec3(C.w, 1.0));
mat3 xyz = transpose(mat3(r, g, b));
vec3 scale = w * inverse(xyz);
mat3 scale_diag = mat3(
scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.z
);
return scale_diag * xyz;
}
mat3 XYZ_to_RGB(Chromaticity C) {
if (C == GRAY)
return SingularY3;
return inverse(RGB_to_XYZ(C));
}
// http://www.brucelindbloom.com/Eqn_ChromAdapt.html
mat3 adaptation(vec2 w1, vec2 w2, mat3 cat) {
vec3 src_xyz = xyY_to_XYZ(vec3(w1, 1.0));
vec3 dst_xyz = xyY_to_XYZ(vec3(w2, 1.0));
vec3 src_lms = src_xyz * cat;
vec3 dst_lms = dst_xyz * cat;
vec3 scale = dst_lms / src_lms;
mat3 scale_diag = mat3(
scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.z
);
return cat * scale_diag * inverse(cat);
}
// CAM16 uses CAT16 as cat and equal-energy illuminant (E) as wt.
// https://www.researchgate.net/publication/318152296_Comprehensive_color_solutions_CAM16_CAT16_and_CAM16-UCS
// Android uses Bradford as cat and D50 as wt.
// https://android.googlesource.com/platform/frameworks/base/+/master/graphics/java/android/graphics/ColorSpace.java
mat3 adaptation_two_step(vec2 w1, vec2 w2, vec2 wt, mat3 cat) {
return adaptation(w1, wt, cat) * adaptation(wt, w2, cat);
}
mat3 adaptation_two_step(vec2 w1, vec2 w2) {
return adaptation_two_step(w1, w2, E, CAT16);
}
mat3 RGB_to_RGB(Chromaticity c1, Chromaticity c2) {
mat3 m = Identity3;
if (c1 != c2) {
m *= RGB_to_XYZ(c1);
if (c1.w != c2.w) {
m *= adaptation_two_step(c1.w, c2.w);
}
m *= XYZ_to_RGB(c2);
}
return m;
}
vec3 RGB_to_RGB(vec3 c, Chromaticity c1, Chromaticity c2) {
return c * RGB_to_RGB(c1, c2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_RGB(color.rgb, from, to);
return color;
}

View file

@ -0,0 +1,104 @@
// Visualizes the out of gamut colors using false color.
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (false color)
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
vec3 cbrt(vec3 color) {
return vec3(
cbrt(color.x),
cbrt(color.y),
cbrt(color.z)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.8190224379967030, 0.3619062600528904, -0.1288737815209879,
0.0329836539323885, 0.9292868615863434, 0.0361446663506424,
0.0481771893596242, 0.2642395317527308, 0.6335478284694309
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.2268798758459243, -0.5578149944602171, 0.2813910456659647,
-0.0405757452148008, 1.1122868032803170, -0.0717110580655164,
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816
);
}
vec3 LMS_to_Lab(vec3 LMS) {
return LMS * mat3(
0.2104542683093140, 0.7936177747023054, -0.0040720430116193,
1.9779985324311684, -2.4285922420485799, 0.4505937096174110,
0.0259040424655478, 0.7827717124575296, -0.8086757549230774
);
}
vec3 Lab_to_LMS(vec3 Lab) {
return Lab * mat3(
1.0000000000000000, 0.3963377773761749, 0.2158037573099136,
1.0000000000000000, -0.1055613458156586, -0.0638541728258133,
1.0000000000000000, -0.0894841775298119, -1.2914855480194092
);
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = cbrt(color);
color = LMS_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_LMS(color);
color = pow(color, vec3(3.0));
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
vec3 BT2020_to_BT709(vec3 color) {
return color * mat3(
1.66049100210843540, -0.58764113878854950, -0.072849863319884740,
-0.12455047452159074, 1.13289989712595960, -0.008349422604369515,
-0.01815076335490526, -0.10057889800800737, 1.118729661362913000
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
vec3 color_dst = BT2020_to_BT709(color.rgb);
vec3 color_dst_cliped = clamp(color_dst, 0.0, 1.0);
bool is_in_gamut = color_dst == color_dst_cliped;
color.rgb = RGB_to_Lab(color.rgb);
color.rgb = is_in_gamut ? vec3(color.x, vec2(0.0)) : vec3(0.5, color.yz);
color.rgb = Lab_to_RGB(color.rgb);
color.rgb = BT2020_to_BT709(color.rgb);
return color;
}

View file

@ -0,0 +1,88 @@
// https://github.com/jedypod/gamut-compress
// https://github.com/ampas/aces-dev/blob/dev/transforms/ctl/lmt/LMT.Academy.ReferenceGamutCompress.ctl
//!PARAM cyan_limit
//!TYPE float
//!MINIMUM 1.01
//!MAXIMUM 2.0
1.216
//!PARAM magenta_limit
//!TYPE float
//!MINIMUM 1.01
//!MAXIMUM 2.0
1.035
//!PARAM yellow_limit
//!TYPE float
//!MINIMUM 1.01
//!MAXIMUM 2.0
1.076
//!PARAM cyan_threshold
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.998
//!PARAM magenta_threshold
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.940
//!PARAM yellow_threshold
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.977
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC gamut mapping (jedypod)
// Parabolic compression function
// https://www.desmos.com/calculator/khowxlu6xh
float parabolic(float x, float t0, float x0, float y0) {
float s = (y0 - t0) / sqrt(x0 - y0);
float ox = t0 - s * s / 4.0;
float oy = t0 - s * sqrt(s * s / 4.0);
return (x < t0 ? x : s * sqrt(x - ox) + oy);
}
vec3 gamut_compress(vec3 rgb) {
// Achromatic axis
float ac = max(max(rgb.r, rgb.g), rgb.b);
// Inverse RGB Ratios: distance from achromatic axis
vec3 d = ac == 0.0 ? vec3(0.0) : (ac - rgb) / abs(ac);
// Compressed distance
vec3 cd = vec3(
parabolic(d.x, cyan_threshold, cyan_limit, 1.0),
parabolic(d.y, magenta_threshold, magenta_limit, 1.0),
parabolic(d.z, yellow_threshold, yellow_limit, 1.0)
);
// Inverse RGB Ratios to RGB
vec3 crgb = ac - cd * abs(ac);
return crgb;
}
vec3 BT2020_to_BT709(vec3 color) {
return color * mat3(
1.66049100210843540, -0.58764113878854950, -0.072849863319884740,
-0.12455047452159074, 1.13289989712595960, -0.008349422604369515,
-0.01815076335490526, -0.10057889800800737, 1.118729661362913000
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = BT2020_to_BT709(color.rgb);
color.rgb = gamut_compress(color.rgb);
return color;
}

View file

@ -0,0 +1,346 @@
// The Academy Color Encoding System (ACES)
// https://github.com/ampas/aces-core/tree/v1.3.1
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC tone mapping (aces)
const float TINY = 1e-5;
const float HALF_POS_INF = 31744.0;
// Gamut Compress Parameters
const float LIM_CYAN = 1.147;
const float LIM_MAGENTA = 1.264;
const float LIM_YELLOW = 1.312;
const float THR_CYAN = 0.815;
const float THR_MAGENTA = 0.803;
const float THR_YELLOW = 0.880;
const float PWR = 1.2;
// "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.0;
const float RRT_RED_WIDTH = 135.0;
// Desaturation constants
const float RRT_SAT_FACTOR = 0.96;
const float ODT_SAT_FACTOR = 0.93;
// Gamma compensation factor
const float DIM_SURROUND_GAMMA = 0.9811;
const mat3 RGB_to_XYZ = mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
const mat3 XYZ_to_RGB = mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
const mat3 AP0_to_XYZ = mat3(
0.9525523959, 0.0000000000, 0.0000936786,
0.3439664498, 0.7281660966, -0.0721325464,
0.0000000000, 0.0000000000, 1.0088251844
);
const mat3 XYZ_to_AP0 = mat3(
1.0498110175, 0.0000000000, -0.0000974845,
-0.4959030231, 1.3733130458, 0.0982400361,
0.0000000000, 0.0000000000, 0.9912520182
);
const mat3 AP1_to_XYZ = mat3(
0.6624541811, 0.1340042065, 0.1561876870,
0.2722287168, 0.6740817658, 0.0536895174,
-0.0055746495, 0.0040607335, 1.0103391003
);
const mat3 XYZ_to_AP1 = mat3(
1.6410233797, -0.3248032942, -0.2364246952,
-0.6636628587, 1.6153315917, 0.0167563477,
0.0117218943, -0.0082844420, 0.9883948585
);
const vec3 LUMINANCE_AP1 = vec3(0.2722287168, 0.6740817658, 0.0536895174);
const mat3 D60_to_D65_CAT = mat3(
0.987224, -0.00611327, 0.0159533,
-0.00759836, 1.00186, 0.00533002,
0.00307257, -0.00509595, 1.08168
);
const mat3 D65_to_D60_CAT = mat3(
1.0130349238541335252, 0.0061053088545854651618, -0.014970963195236360098,
0.0076982295895192892886, 0.9981648317745535941, -0.0050320341346474782061,
-0.0028413125165573776196, 0.0046851555780399034147, 0.92450665292696206889
);
// Power compression function
// https://www.desmos.com/calculator/iwcyjg6av0
float compress(float dist, float lim, float thr, float pwr) {
float scl = (lim - thr) / pow(pow((1.0 - thr) / (lim - thr), -pwr) - 1.0, 1.0 / pwr);
float c = thr + (dist - thr) / (pow(1.0 + pow((dist - thr) / scl, pwr), 1.0 / pwr));
return (dist < thr ? dist : c);
}
vec3 reference_gamut_compress(vec3 rgb) {
// Achromatic axis
float ac = max(max(rgb.r, rgb.g), rgb.b);
// Inverse RGB Ratios: distance from achromatic axis
vec3 d = ac == 0.0 ? vec3(0.0) : (ac - rgb) / abs(ac);
// Compressed distance
vec3 cd = vec3(
compress(d.x, LIM_CYAN, THR_CYAN, PWR),
compress(d.y, LIM_MAGENTA, THR_MAGENTA, PWR),
compress(d.z, LIM_YELLOW, THR_YELLOW, PWR)
);
// Inverse RGB Ratios to RGB
vec3 crgb = ac - cd * abs(ac);
return crgb;
}
float rgb_to_saturation(vec3 rgb) {
float mi = min(min(rgb.r, rgb.g), rgb.b);
float ma = max(max(rgb.r, rgb.g), rgb.b);
return (max(ma, TINY) - max(mi, TINY)) / max(ma, 1e-2);
}
// 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_to_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.
float rgb_to_yc(vec3 rgb) {
float ycRadiusWeight = 1.75;
float r = rgb.r;
float g = rgb.g;
float b = rgb.b;
float chroma = sqrt(b * (b - g) + g * (g - r) + r * (r - b));
return (b + g + r + ycRadiusWeight * chroma) / 3.0;
}
// Sigmoid function in the range 0 to 1 spanning -2 to +2.
float sigmoid_shaper(float x) {
float t = max(1.0 - abs(x / 2.0), 0.0);
float y = 1.0 + sign(x) * (1.0 - t * t);
return y / 2.0;
}
float glow_fwd(float ycIn, float glowGainIn, float glowMid) {
float 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;
}
// 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.
float rgb_to_hue(vec3 rgb) {
// RGB triplets where RGB are equal have an undefined hue
float hue = 0.0;
if (!(rgb.x == rgb.y && rgb.y == rgb.z)) {
float x = sqrt(3.0) * (rgb.y - rgb.z);
float y = 2.0 * rgb.x - rgb.y - rgb.z;
hue = degrees(atan(y, x));
}
return (hue < 0.0) ? hue + 360.0 : hue;
}
float center_hue(float hue, float centerH) {
float hueCentered = hue - centerH;
if (hueCentered < -180.0) {
hueCentered = hueCentered + 360.0;
} else if (hueCentered > 180.0) {
hueCentered = hueCentered - 360.0;
}
return hueCentered;
}
// Fitting of RRT + ODT (RGB monitor 100 nits dim) from:
// https://github.com/colour-science/colour-unity/blob/master/Assets/Colour/Notebooks/CIECAM02_Unity.ipynb
// RMSE: 0.0012846272106
vec3 tonescale(vec3 ap1) {
float a = 2.785085;
float b = 0.107772;
float c = 2.936045;
float d = 0.887122;
float e = 0.806889;
return (ap1 * (a * ap1 + b)) / (ap1 * (c * ap1 + d) + e);
}
vec3 XYZ_to_xyY(vec3 XYZ) {
float X = XYZ.x;
float Y = XYZ.y;
float Z = XYZ.z;
float divisor = X + Y + Z;
if (divisor == 0.0) divisor = 1e-6;
float x = X / divisor;
float y = Y / divisor;
return vec3(x, y, Y);
}
vec3 xyY_to_XYZ(vec3 xyY) {
float x = xyY.x;
float y = xyY.y;
float Y = xyY.z;
float multiplo = Y / max(y, 1e-6);
float z = 1.0 - x - y;
float X = x * multiplo;
float Z = z * multiplo;
return vec3(X, Y, Z);
}
vec3 darkSurround_to_dimSurround(vec3 linearCV) {
vec3 XYZ = linearCV * AP1_to_XYZ;
vec3 xyY = XYZ_to_xyY(XYZ);
xyY.z = clamp(xyY.z, 0.0, HALF_POS_INF);
xyY.z = pow(xyY.z, DIM_SURROUND_GAMMA);
XYZ = xyY_to_XYZ(xyY);
return XYZ * XYZ_to_AP1;
}
vec3 ACES(vec3 color) {
vec3 ap0;
vec3 ap1;
vec3 cv;
// Look Modification Transforms (LMTs)
ap1 = color * RGB_to_XYZ * D65_to_D60_CAT * XYZ_to_AP1;
ap1 = reference_gamut_compress(ap1);
ap0 = ap1 * AP1_to_XYZ * XYZ_to_AP0;
// Reference Rendering Transform (RRT)
// Glow module
float saturation = rgb_to_saturation(ap0);
float ycIn = rgb_to_yc(ap0);
float s = sigmoid_shaper((saturation - 0.4) / 0.2);
float addedGlow = 1.0 + glow_fwd(ycIn, RRT_GLOW_GAIN * s, RRT_GLOW_MID);
ap0 *= addedGlow;
// Red modifier
float hue = rgb_to_hue(ap0);
float centeredHue = center_hue(hue, RRT_RED_HUE);
// hueWeight = cubic_basis_shaper(centeredHue, RRT_RED_WIDTH);
float hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centeredHue / RRT_RED_WIDTH));
hueWeight *= hueWeight;
ap0.r += hueWeight * saturation * (RRT_RED_PIVOT - ap0.r) * (1.0 - RRT_RED_SCALE);
// ACES to RGB rendering space
ap1 = ap0 * AP0_to_XYZ * XYZ_to_AP1;
// avoids saturated negative colors from becoming positive in the matrix
ap1 = clamp(ap1, 0.0, HALF_POS_INF);
// Global desaturation
ap1 = mix(vec3(dot(ap1, LUMINANCE_AP1)), ap1, RRT_SAT_FACTOR);
// Output Device Transform (ODT)
// Apply the tonescale independently in rendering-space RGB
ap1 = tonescale(ap1);
// Apply gamma adjustment to compensate for dim surround
cv = darkSurround_to_dimSurround(ap1);
// Apply desaturation to compensate for luminance difference
cv = mix(vec3(dot(cv, LUMINANCE_AP1)), cv, ODT_SAT_FACTOR);
// Convert to display primary encoding
cv = cv * AP1_to_XYZ * D60_to_D65_CAT * XYZ_to_RGB;
return cv;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = ACES(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC black point compensation
// https://www.color.org/WP40-Black_Point_Compensation_2010-07-27.pdf
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 black_point_compensation(vec3 XYZ, float s, float d) {
float r = (1.0 - d) / (1.0 - s);
return r * XYZ + (1.0 - r) * RGB_to_XYZ(vec3(1.0));
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_XYZ(color.rgb);
color.rgb = black_point_compensation(color.rgb, 0.0, 0.001);
color.rgb = XYZ_to_RGB(color.rgb);
return color;
}

View file

@ -0,0 +1,936 @@
// Astra, a tone mapping operator designed to preserve the creator's intent
// shoulder segment: http://filmicworlds.com/blog/filmic-tonemapping-with-piecewise-power-curves/
// toe segment: https://technorgb.blogspot.com/2018/02/hyperbola-tone-mapping.html
// working space: https://doi.org/10.1364/OE.25.015131
// hk effect: https://doi.org/10.1364/OE.534073
// chroma correction: https://www.itu.int/pub/R-REP-BT.2408
// dynamic metadata: https://github.com/mpv-player/mpv/pull/15239
// fast gaussian blur: https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
//!PARAM min_luma
//!TYPE float
0.0
//!PARAM max_luma
//!TYPE float
0.0
//!PARAM max_cll
//!TYPE float
0.0
//!PARAM max_fall
//!TYPE float
0.0
//!PARAM scene_max_r
//!TYPE float
0.0
//!PARAM scene_max_g
//!TYPE float
0.0
//!PARAM scene_max_b
//!TYPE float
0.0
//!PARAM scene_avg
//!TYPE float
0.0
//!PARAM max_pq_y
//!TYPE float
0.0
//!PARAM avg_pq_y
//!TYPE float
0.0
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!PARAM auto_exposure_anchor
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.75
//!PARAM hk_effect_compensate_scaling
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
1.0
//!PARAM chroma_correction_scaling
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
1.0
//!PARAM spatial_stable_iterations
//!TYPE uint
//!MINIMUM 0
//!MAXIMUM 8
2
//!PARAM temporal_stable_frames
//!TYPE uint
//!MINIMUM 0
//!MAXIMUM 120
8
//!PARAM enable_metering
//!TYPE uint
//!MINIMUM 0
//!MAXIMUM 1
1
//!PARAM preview_metering
//!TYPE uint
//!MINIMUM 0
//!MAXIMUM 1
0
//!BUFFER METERED
//!VAR uint metered_max_i
//!STORAGE
//!BUFFER METERED_TEMPORAL
//!VAR uint metered_max_i_t[128]
//!STORAGE
//!HOOK OUTPUT
//!BIND HOOKED
//!SAVE METERING
//!COMPONENTS 1
//!WIDTH 512
//!HEIGHT 288
//!WHEN enable_metering 0 > max_pq_y 0 = * scene_max_r 0 = * scene_max_g 0 = * scene_max_b 0 = *
//!DESC metering (feature map)
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
float l = dot(color.rgb * reference_white, y_coef);
float i = pq_eotf_inv(l);
return vec4(i, vec3(0.0));
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 0 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 0 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 1 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 1 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 2 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 2 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 3 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 3 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 4 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 4 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 5 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 5 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 6 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 6 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 7 >
//!DESC metering (spatial stabilization, blur, horizonal)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(1.0, 0.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!SAVE METERING
//!WHEN spatial_stable_iterations 7 >
//!DESC metering (spatial stabilization, blur, vertical)
const vec4 offset = vec4(0.0, 1.411764705882353, 3.2941176470588234, 5.176470588235294);
const vec4 weight = vec4(0.1964825501511404, 0.2969069646728344, 0.09447039785044732, 0.010381362401148057);
const vec2 direction = vec2(0.0, 1.0);
vec4 hook(){
uint i = 0;
vec4 c = METERING_texOff(offset[i]) * weight[i];
for (i = 1; i < 4; i++) {
c += METERING_texOff( direction * offset[i]) * weight[i];
c += METERING_texOff(-direction * offset[i]) * weight[i];
}
return c;
}
//!HOOK OUTPUT
//!BIND METERING
//!BIND METERED
//!SAVE EMPTY
//!COMPUTE 32 32
//!DESC metering (data, max)
shared uint local_max;
void hook() {
if (gl_GlobalInvocationID.x == 0 && gl_GlobalInvocationID.y == 0) {
metered_max_i = 0;
}
if (gl_LocalInvocationIndex == 0) {
local_max = 0;
}
memoryBarrierShared();
barrier();
float value = METERING_tex(METERING_pos).x;
uint rounded = uint(value * 4095.0 + 0.5);
atomicMax(local_max, rounded);
memoryBarrierShared();
barrier();
if (gl_LocalInvocationIndex == 0) {
atomicMax(metered_max_i, local_max);
}
}
//!HOOK OUTPUT
//!BIND METERING
//!BIND METERED
//!BIND METERED_TEMPORAL
//!SAVE EMPTY
//!WIDTH 1
//!HEIGHT 1
//!COMPUTE 1 1
//!WHEN temporal_stable_frames
//!DESC metering (temporal stabilization)
void temporal_prepend() {
for (uint i = temporal_stable_frames - 1; i > 0; i--) {
metered_max_i_t[i] = metered_max_i_t[i - 1];
}
metered_max_i_t[0] = metered_max_i;
}
float temporal_harmonic_mean() {
float sum = 0.0;
for (uint i = 0; i < temporal_stable_frames; i++) {
float current = float(metered_max_i_t[i]);
sum += 1.0 / max(current, 1e-6);
}
return temporal_stable_frames / sum;
}
void temporal_fill() {
for (uint i = 0; i < temporal_stable_frames; i++) {
metered_max_i_t[i] = metered_max_i;
}
}
float temporal_predict() {
float sum_x = 0.0;
float sum_y = 0.0;
float sum_x2 = 0.0;
float sum_xy = 0.0;
float n = temporal_stable_frames;
float xp = float(n + 1);
for (int i = 0; i < n; i++) {
float x = float(i + 1);
sum_x += x;
sum_y += metered_max_i_t[i];
sum_x2 += x * x;
sum_xy += x * metered_max_i_t[i];
}
float a = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
float b = (sum_y - a * sum_x) / float(n);
return a * xp + b;
}
bool is_sence_changed(float m, float p) {
float black = 16.0;
if (black > metered_max_i)
return true;
float tolerance = 36.0;
float im = float(m) / 4095.0;
float ip = float(p) / 4095.0;
float delta = 720 * abs(im - ip);
return delta > tolerance;
}
void hook() {
float p = temporal_predict();
temporal_prepend();
float m = temporal_harmonic_mean();
if (is_sence_changed(m, p)) {
temporal_fill();
return;
}
metered_max_i = uint(m + 0.5);
}
//!HOOK OUTPUT
//!BIND HOOKED
//!BIND METERING
//!BIND METERED
//!WHEN preview_metering
//!DESC metering (preview)
bool almost_equal(float a, float b, float epsilon) {
return abs(a - b) < epsilon;
}
vec4 hook() {
float metering = METERING_tex(METERING_pos).x;
float lmi = float(metered_max_i) / 4095.0;
vec3 color = vec3(metering);
float delta = 720 * abs(metering - lmi);
if (delta < 4.0)
color = vec3(1.0, 0.0, 0.0);
if (almost_equal(1.0 - METERING_pos.y, lmi, 1e-3))
color = vec3(0.0, 1.0, 0.0);
return vec4(color, 1.0);
}
//!HOOK OUTPUT
//!BIND HOOKED
//!BIND METERED
//!DESC tone mapping (astra)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.0 , 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.25336628137366,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
const float b = 1.15;
const float g = 0.66;
vec3 XYZ_to_XYZm(vec3 XYZ) {
float Xm = (b * XYZ.x) - ((b - 1.0) * XYZ.z);
float Ym = (g * XYZ.y) - ((g - 1.0) * XYZ.x);
return vec3(Xm, Ym, XYZ.z);
}
vec3 XYZm_to_XYZ(vec3 XYZm) {
float Xa = (XYZm.x + ((b - 1.0) * XYZm.z)) / b;
float Ya = (XYZm.y + ((g - 1.0) * Xa)) / g;
return vec3(Xa, Ya, XYZm.z);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.41478972, 0.579999, 0.0146480,
-0.2015100, 1.120649, 0.0531008,
-0.0166008, 0.264800, 0.6684799
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.9242264357876067, -1.0047923125953657, 0.037651404030618,
0.35031676209499907, 0.7264811939316552, -0.06538442294808501,
-0.09098281098284752, -0.3127282905230739, 1.5227665613052603
);
}
vec3 LMS_to_Iab(vec3 LMS) {
return LMS * mat3(
0.5, 0.5, 0.0,
3.524000, -4.066708, 0.542708,
0.199076, 1.096799, -1.295875
);
}
vec3 Iab_to_LMS(vec3 Iab) {
return Iab * mat3(
1.0, 0.1386050432715393, 0.0580473161561189,
1.0, -0.1386050432715393, -0.0580473161561189,
1.0, -0.0960192420263190, -0.8118918960560390
);
}
const float d = -0.56;
const float d0 = 1.6295499532821566e-11;
float I_to_J(float I) {
return ((1.0 + d) * I) / (1.0 + (d * I)) - d0;
}
float J_to_I(float J) {
return (J + d0) / (1.0 + d - d * (J + d0));
}
float hke_fh_hellwig(float h, float a1, float a2, float a3, float a4, float a5) {
return a1 * cos(h) + a2 * cos(2.0 * h) + a3 * sin(h) + a4 * sin(2.0 * h) + a5;
}
float hke_fh_high(float h, float k1, float k2, float k3, float k4) {
h = mod(mod(degrees(h), 360.0) + 360.0, 360.0);
float by = k1 * abs(sin(radians((h - 90.0)/ 2.0))) + k2;
float r = h <= 90.0 || h >= 270.0 ? k3 * abs(cos(radians(h))) + k4 : 0.0;
return by + r;
}
float hke_fh_liao(float h, float k3, float k4, float k5) {
h = mod(mod(degrees(h), 360.0) + 360.0, 360.0);
return k3 * abs(log(((h + k4) / (90.0 + k4)))) + k5;
}
float hke_fh(float h) {
float result = hke_fh_liao(h, 0.3495, 45.0, 0.1567);
return result * hk_effect_compensate_scaling;
}
float J_to_Jhk(vec3 JCh) {
float J = JCh.x;
float C = JCh.y;
float h = JCh.z;
return J + C * hke_fh(h);
}
float Jhk_to_J(vec3 JCh) {
float J = JCh.x;
float C = JCh.y;
float h = JCh.z;
return J - C * hke_fh(h);
}
// https://github.com/color-js/color.js/pull/629
const float epsilon = 0.0002363;
vec3 Lab_to_LCh(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float C = length(vec2(a, b));
float h = (abs(a) < epsilon && abs(b) < epsilon) ? 0.0 : atan(b, a);
return vec3(L, C, h);
}
vec3 LCh_to_Lab(vec3 LCh) {
float L = LCh.x;
float C = LCh.y;
float h = LCh.z;
C = max(C, 0.0);
float a = C * cos(h);
float b = C * sin(h);
return vec3(L, a, b);
}
vec3 RGB_to_Jab(vec3 color) {
color *= reference_white;
color = RGB_to_XYZ(color);
color = XYZ_to_XYZm(color);
color = XYZ_to_LMS(color);
color = pq_eotf_inv(color);
color = LMS_to_Iab(color);
color.x = I_to_J(color.x);
color.x = J_to_Jhk(Lab_to_LCh(color));
return color;
}
vec3 Jab_to_RGB(vec3 color) {
color.x = Jhk_to_J(Lab_to_LCh(color));
color.x = J_to_I(color.x);
color = Iab_to_LMS(color);
color = pq_eotf(color);
color = LMS_to_XYZ(color);
color = XYZm_to_XYZ(color);
color = XYZ_to_RGB(color);
color /= reference_white;
return color;
}
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(RGB_to_XYZ(scene_max_rgb).y);
}
if (enable_metering > 0)
return float(metered_max_i) / 4095.0;
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float get_avg_i() {
if (avg_pq_y > 0.0)
return avg_pq_y;
if (scene_avg > 0.0)
return pq_eotf_inv(scene_avg);
// if (max_fall > 0.0)
// return pq_eotf_inv(max_fall);
return 0.0;
}
float ev = 0.0;
vec3 auto_exposure(vec3 color) {
if (auto_exposure_anchor <= 0.0)
return color;
float avg_i = get_avg_i();
if (avg_i <= 0.0)
return color;
float ach = pq_eotf(J_to_I(
auto_exposure_anchor *
I_to_J(pq_eotf_inv(reference_white))
));
float avg = pq_eotf(avg_i);
float mxx = pq_eotf(get_max_i());
float ref = reference_white;
float old = 100.0;
float ev_min = min(log2(max(ref / mxx, 1e-6)), 0.0);
float ev_max = max(log2(max(ref / old, 1e-6)), 0.0);
ev = log2(max(ach / avg, 1e-6));
ev = clamp(ev, ev_min, ev_max);
return color * exp2(ev);
}
float f(float x, float iw, float ib, float ow, float ob) {
float midgray = 0.5 * ow;
float shadow = mix(midgray, ob, 0.66);
float highlight = mix(midgray, ow, 0.04);
float x0 = ib;
float y0 = ob;
float x1 = shadow;
float y1 = shadow;
float x2 = highlight;
float y2 = highlight;
float x3 = iw;
float y3 = ow;
float al = (y2 - y1) / (x2 - x1);
if (x < x1) {
float at = al * (x1 - x0) * (x1 - x0) * (y1 - y0) * (y1 - y0) / ((y1 - y0 - al * (x1 - x0)) * (y1 - y0 - al * (x1 - x0)));
float bt = al * (x1 - x0) * (x1 - x0) / (y1 - y0 - al * (x1 - x0));
float ct = (y1 - y0) * (y1 - y0) / (y1 - y0 - al * (x1 - x0));
x = -at / (x - x0 + bt) + ct + y0;
} else if (x < x2) {
float bl = y1 - al * x1;
x = al * x + bl;
} else {
float bs = al * (x3 - x2) / (y3 - y2);
float as = log(y3 - y2) - bs * log(x3 - x2);
x = -exp(as + bs * log(max(-(x - x3), 1e-6))) + y3;
}
return clamp(x, ob, ow);
}
float curve(float x) {
float ow = I_to_J(pq_eotf_inv(reference_white));
float ob = I_to_J(pq_eotf_inv(reference_white / 1000.0));
float iw = get_max_i();
float ib = get_min_i();
if (ev != 0.0) {
iw = pq_eotf_inv(pq_eotf(iw) * exp2(ev));
ib = pq_eotf_inv(pq_eotf(ib) * exp2(ev));
}
iw = max(I_to_J(iw), ow + 1e-3);
ib = min(I_to_J(ib), ob - 1e-3);
return f(x, iw, ib, ow, ob);
}
vec2 chroma_correction(vec2 ab, float i1, float i2) {
float r1 = i1 / max(i2, 1e-6);
float r2 = i2 / max(i1, 1e-6);
return ab * mix(1.0, min(r1, r2), chroma_correction_scaling);
}
vec3 tone_mapping(vec3 iab) {
float i2 = curve(iab.x);
vec2 ab2 = chroma_correction(iab.yz, iab.x, i2);
return vec3(i2, ab2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = auto_exposure(color.rgb);
color.rgb = RGB_to_Jab(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = Jab_to_RGB(color.rgb);
return color;
}

View file

@ -0,0 +1,764 @@
// ITU-R BT.2390 EETF
// https://www.itu.int/pub/R-REP-BT.2390
// https://www.itu.int/pub/R-REP-BT.2408
//!PARAM min_luma
//!TYPE float
0.0
//!PARAM max_luma
//!TYPE float
0.0
//!PARAM max_cll
//!TYPE float
0.0
//!PARAM scene_max_r
//!TYPE float
0.0
//!PARAM scene_max_g
//!TYPE float
0.0
//!PARAM scene_max_b
//!TYPE float
0.0
//!PARAM max_pq_y
//!TYPE float
0.0
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!PARAM chroma_correction_scaling
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
1.0
//!PARAM representation
//!TYPE ENUM int
ictcp
ycbcr
yrgb
prergb
maxrgb
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN representation 0 =
//!DESC tone mapping (bt.2390, ICtCp)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.0 , 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.25336628137366,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.3592832590121217, 0.6976051147779502, -0.0358915932320290,
-0.1920808463704993, 1.1004767970374321, 0.0753748658519118,
0.0070797844607479, 0.0748396662186362, 0.8433265453898765
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
2.0701522183894223, -1.3263473389671563, 0.2066510476294053,
0.3647385209748072, 0.6805660249472273, -0.0453045459220347,
-0.0497472075358123, -0.0492609666966131, 1.1880659249923042
);
}
vec3 LMS_to_ICtCp(vec3 LMS) {
return LMS * mat3(
2048.0 / 4096.0, 2048.0 / 4096.0, 0.0 / 4096.0,
6610.0 / 4096.0, -13613.0 / 4096.0, 7003.0 / 4096.0,
17933.0 / 4096.0, -17390.0 / 4096.0, -543.0 / 4096.0
);
}
vec3 ICtCp_to_LMS(vec3 ICtCp) {
return ICtCp * mat3(
1.0, 0.0086090370379328, 0.1110296250030260,
1.0, -0.0086090370379328, -0.1110296250030260,
1.0, 0.5600313357106791, -0.3206271749873189
);
}
vec3 RGB_to_ICtCp(vec3 color) {
color *= reference_white;
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = pq_eotf_inv(color);
color = LMS_to_ICtCp(color);
return color;
}
vec3 ICtCp_to_RGB(vec3 color) {
color = ICtCp_to_LMS(color);
color = pq_eotf(color);
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
color /= reference_white;
return color;
}
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(RGB_to_XYZ(scene_max_rgb).y);
}
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float f(float x, float iw, float ib, float ow, float ob) {
float minLum = (ob - ib) / (iw - ib);
float maxLum = (ow - ib) / (iw - ib);
float KS = 1.5 * maxLum - 0.5;
float b = minLum;
// E1
x = (x - ib) / (iw - ib);
// E2
if (KS <= x) {
float TB = (x - KS) / (1.0 - KS);
float TB2 = TB * TB;
float TB3 = TB * TB2;
float PB = (2.0 * TB3 - 3.0 * TB2 + 1.0) * KS +
(TB3 - 2.0 * TB2 + TB) * (1.0 - KS) +
(-2.0 * TB3 + 3.0 * TB2) * maxLum;
x = PB;
}
// E3
if (0.0 <= x) {
x = x + b * pow((1.0 - x), 4.0);
}
// E4
x = x * (iw - ib) + ib;
return clamp(x, ob, ow);
}
float curve(float x) {
float ow = pq_eotf_inv(reference_white);
float ob = pq_eotf_inv(reference_white / 1000.0);
float iw = max(get_max_i(), ow + 1e-3);
float ib = min(get_min_i(), ob - 1e-3);
return f(x, iw, ib, ow, ob);
}
vec2 chroma_correction(vec2 ab, float i1, float i2) {
float r1 = i1 / max(i2, 1e-6);
float r2 = i2 / max(i1, 1e-6);
return ab * mix(1.0, min(r1, r2), chroma_correction_scaling);
}
vec3 tone_mapping(vec3 iab) {
float i2 = curve(iab.x);
vec2 ab2 = chroma_correction(iab.yz, iab.x, i2);
return vec3(i2, ab2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_ICtCp(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = ICtCp_to_RGB(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN representation 1 =
//!DESC tone mapping (bt.2390, Y'Cb'Cr')
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
const float a = y_coef.r;
const float b = y_coef.g;
const float c = y_coef.b;
const float d = 2.0 * (1.0 - c);
const float e = 2.0 * (1.0 - a);
vec3 RGB_to_YCbCr(vec3 RGB) {
return RGB * mat3(
a, b, c,
-a / d, -b / d, 0.5,
0.5, -b / e, -c / e
);
}
vec3 YCbCr_to_RGB(vec3 YCbCr) {
return YCbCr * mat3(
1.0, 0.0, e,
1.0, -c / b * d, -a / b * e,
1.0, d, 0.0
);
}
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(dot(scene_max_rgb, y_coef));
}
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float f(float x, float iw, float ib, float ow, float ob) {
float minLum = (ob - ib) / (iw - ib);
float maxLum = (ow - ib) / (iw - ib);
float KS = 1.5 * maxLum - 0.5;
float b = minLum;
// E1
x = (x - ib) / (iw - ib);
// E2
if (KS <= x) {
float TB = (x - KS) / (1.0 - KS);
float TB2 = TB * TB;
float TB3 = TB * TB2;
float PB = (2.0 * TB3 - 3.0 * TB2 + 1.0) * KS +
(TB3 - 2.0 * TB2 + TB) * (1.0 - KS) +
(-2.0 * TB3 + 3.0 * TB2) * maxLum;
x = PB;
}
// E3
if (0.0 <= x) {
x = x + b * pow((1.0 - x), 4.0);
}
// E4
x = x * (iw - ib) + ib;
return clamp(x, ob, ow);
}
float curve(float x) {
float ow = pq_eotf_inv(reference_white);
float ob = pq_eotf_inv(reference_white / 1000.0);
float iw = max(get_max_i(), ow + 1e-3);
float ib = min(get_min_i(), ob - 1e-3);
return f(x, iw, ib, ow, ob);
}
vec2 chroma_correction(vec2 ab, float i1, float i2) {
float r1 = i1 / max(i2, 1e-6);
float r2 = i2 / max(i1, 1e-6);
return ab * mix(1.0, min(r1, r2), chroma_correction_scaling);
}
vec3 tone_mapping(vec3 iab) {
float i2 = curve(iab.x);
vec2 ab2 = chroma_correction(iab.yz, iab.x, i2);
return vec3(i2, ab2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = pq_eotf_inv(color.rgb * reference_white);
color.rgb = RGB_to_YCbCr(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = YCbCr_to_RGB(color.rgb);
color.rgb = pq_eotf(color.rgb) / reference_white;
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN representation 2 =
//!DESC tone mapping (bt.2390, YRGB)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(dot(scene_max_rgb, y_coef));
}
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float f(float x, float iw, float ib, float ow, float ob) {
float minLum = (ob - ib) / (iw - ib);
float maxLum = (ow - ib) / (iw - ib);
float KS = 1.5 * maxLum - 0.5;
float b = minLum;
// E1
x = (x - ib) / (iw - ib);
// E2
if (KS <= x) {
float TB = (x - KS) / (1.0 - KS);
float TB2 = TB * TB;
float TB3 = TB * TB2;
float PB = (2.0 * TB3 - 3.0 * TB2 + 1.0) * KS +
(TB3 - 2.0 * TB2 + TB) * (1.0 - KS) +
(-2.0 * TB3 + 3.0 * TB2) * maxLum;
x = PB;
}
// E3
if (0.0 <= x) {
x = x + b * pow((1.0 - x), 4.0);
}
// E4
x = x * (iw - ib) + ib;
return clamp(x, ob, ow);
}
float curve(float x) {
float ow = pq_eotf_inv(reference_white);
float ob = pq_eotf_inv(reference_white / 1000.0);
float iw = max(get_max_i(), ow + 1e-3);
float ib = min(get_min_i(), ob - 1e-3);
return f(x, iw, ib, ow, ob);
}
vec3 tone_mapping(vec3 rgb) {
float y1 = dot(rgb, y_coef) * reference_white;
float y2 = pq_eotf(curve(pq_eotf_inv(y1)));
return (y2 / max(y1, 1e-6)) * rgb;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = tone_mapping(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN representation 3 =
//!DESC tone mapping (bt.2390, R'G'B')
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(dot(scene_max_rgb, y_coef));
}
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float f(float x, float iw, float ib, float ow, float ob) {
float minLum = (ob - ib) / (iw - ib);
float maxLum = (ow - ib) / (iw - ib);
float KS = 1.5 * maxLum - 0.5;
float b = minLum;
// E1
x = (x - ib) / (iw - ib);
// E2
if (KS <= x) {
float TB = (x - KS) / (1.0 - KS);
float TB2 = TB * TB;
float TB3 = TB * TB2;
float PB = (2.0 * TB3 - 3.0 * TB2 + 1.0) * KS +
(TB3 - 2.0 * TB2 + TB) * (1.0 - KS) +
(-2.0 * TB3 + 3.0 * TB2) * maxLum;
x = PB;
}
// E3
if (0.0 <= x) {
x = x + b * pow((1.0 - x), 4.0);
}
// E4
x = x * (iw - ib) + ib;
return clamp(x, ob, ow);
}
float curve(float x) {
float ow = pq_eotf_inv(reference_white);
float ob = pq_eotf_inv(reference_white / 1000.0);
float iw = max(get_max_i(), ow + 1e-3);
float ib = min(get_min_i(), ob - 1e-3);
return f(x, iw, ib, ow, ob);
}
vec3 tone_mapping(vec3 rgb) {
return vec3(
curve(rgb.r),
curve(rgb.g),
curve(rgb.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = pq_eotf_inv(color.rgb * reference_white);
color.rgb = tone_mapping(color.rgb);
color.rgb = pq_eotf(color.rgb) / reference_white;
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN representation 4 =
//!DESC tone mapping (bt.2390, maxRGB)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(dot(scene_max_rgb, y_coef));
}
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float f(float x, float iw, float ib, float ow, float ob) {
float minLum = (ob - ib) / (iw - ib);
float maxLum = (ow - ib) / (iw - ib);
float KS = 1.5 * maxLum - 0.5;
float b = minLum;
// E1
x = (x - ib) / (iw - ib);
// E2
if (KS <= x) {
float TB = (x - KS) / (1.0 - KS);
float TB2 = TB * TB;
float TB3 = TB * TB2;
float PB = (2.0 * TB3 - 3.0 * TB2 + 1.0) * KS +
(TB3 - 2.0 * TB2 + TB) * (1.0 - KS) +
(-2.0 * TB3 + 3.0 * TB2) * maxLum;
x = PB;
}
// E3
if (0.0 <= x) {
x = x + b * pow((1.0 - x), 4.0);
}
// E4
x = x * (iw - ib) + ib;
return clamp(x, ob, ow);
}
float curve(float x) {
float ow = pq_eotf_inv(reference_white);
float ob = pq_eotf_inv(reference_white / 1000.0);
float iw = max(get_max_i(), ow + 1e-3);
float ib = min(get_min_i(), ob - 1e-3);
return f(x, iw, ib, ow, ob);
}
vec3 tone_mapping(vec3 rgb) {
float m1 = max(max(rgb.r, rgb.g), rgb.b) * reference_white;
float m2 = pq_eotf(curve(pq_eotf_inv(m1)));
return (m2 / max(m1, 1e-6)) * rgb;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = tone_mapping(color.rgb);
return color;
}

View file

@ -0,0 +1,142 @@
// ITU-R BT.2446 Conversion Method A
// https://www.itu.int/pub/R-REP-BT.2446
//!PARAM max_luma
//!TYPE float
0.0
//!PARAM max_cll
//!TYPE float
0.0
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC tone mapping (bt.2446a)
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
const float a = y_coef.r;
const float b = y_coef.g;
const float c = y_coef.b;
const float d = 2.0 * (1.0 - c);
const float e = 2.0 * (1.0 - a);
vec3 RGB_to_YCbCr(vec3 RGB) {
return RGB * mat3(
a, b, c,
-a / d, -b / d, 0.5,
0.5, -b / e, -c / e
);
}
vec3 YCbCr_to_RGB(vec3 YCbCr) {
return YCbCr * mat3(
1.0, 0.0, e,
1.0, -c / b * d, -a / b * e,
1.0, d, 0.0
);
}
float get_max_l() {
if (max_cll > 0.0)
return max_cll;
if (max_luma > 0.0)
return max_luma;
return 1000.0;
}
float f(float Y) {
Y = pow(Y, 1.0 / 2.4);
float pHDR = 1.0 + 32.0 * pow(get_max_l() / 10000.0, 1.0 / 2.4);
float pSDR = 1.0 + 32.0 * pow(reference_white / 10000.0, 1.0 / 2.4);
float Yp = log(1.0 + (pHDR - 1.0) * Y) / log(pHDR);
float Yc;
if (Yp <= 0.7399) Yc = Yp * 1.0770;
else if (Yp < 0.9909) Yc = Yp * (-1.1510 * Yp + 2.7811) - 0.6302;
else Yc = Yp * 0.5000 + 0.5000;
float Ysdr = (pow(pSDR, Yc) - 1.0) / (pSDR - 1.0);
Y = pow(Ysdr, 2.4);
return Y;
}
float curve(float Y) {
return f(Y);
}
vec3 tone_mapping(vec3 YCbCr) {
YCbCr /= get_max_l() / reference_white;
float Y = YCbCr.r;
float Cb = YCbCr.g;
float Cr = YCbCr.b;
float Ysdr = curve(Y);
float Yr = Ysdr / max(1.1 * Y, 1e-6);
Cb *= Yr;
Cr *= Yr;
Y = Ysdr - max(0.1 * Cr, 0.0);
return vec3(Y, Cb, Cr);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_YCbCr(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = YCbCr_to_RGB(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC black point compensation
// https://www.color.org/WP40-Black_Point_Compensation_2010-07-27.pdf
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 black_point_compensation(vec3 XYZ, float s, float d) {
float r = (1.0 - d) / (1.0 - s);
return r * XYZ + (1.0 - r) * RGB_to_XYZ(vec3(1.0));
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_XYZ(color.rgb);
color.rgb = black_point_compensation(color.rgb, 0.0, 0.001);
color.rgb = XYZ_to_RGB(color.rgb);
return color;
}

View file

@ -0,0 +1,315 @@
// ITU-R BT.2446 Conversion Method C
// https://www.itu.int/pub/R-REP-BT.2446
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!PARAM alpha
//!TYPE float
//!MINIMUM 0.00
//!MAXIMUM 0.33
0.04
//!PARAM sigma
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.33
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN alpha
//!DESC tone mapping (bt.2446c, crosstalk)
// The crosstalk matrix is applied such that saturations of linear signals are reduced to achromatic to
// avoid hue changes caused by clipping of compressed highlight parts.
vec3 crosstalk(vec3 x, float a) {
float b = 1.0 - 2.0 * a;
mat3 transform = mat3(
b, a, a,
a, b, a,
a, a, b
);
return x * transform;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = crosstalk(color.rgb, alpha);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN sigma
//!DESC tone mapping (bt.2446c, chroma correction)
// Optional processing of chroma correction above HDR Reference White
// In SDR production, highlight parts are sometimes intentionally expressed as white. The processing
// described in this section is optionally used to shift chroma above HDR Reference White to achromatic
// when the converted SDR content requires a degree of consistency for SDR production content. This
// processing is applied as needed before the tone-mapping processing.
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
const float delta = 6.0 / 29.0;
const float deltac = delta * 2.0 / 3.0;
float f(float x) {
return x > pow(delta, 3.0) ?
cbrt(x) :
deltac + x / (3.0 * pow(delta, 2.0));
}
vec3 f(vec3 x) {
return vec3(f(x.x), f(x.y), f(x.z));
}
float f_inv(float x) {
return x > delta ?
pow(x, 3.0) :
(x - deltac) * (3.0 * pow(delta, 2.0));
}
vec3 f_inv(vec3 x) {
return vec3(f_inv(x.x), f_inv(x.y), f_inv(x.z));
}
const vec3 XYZn = vec3(0.95047, 1.00000, 1.08883);
vec3 XYZ_to_Lab(vec3 XYZ) {
XYZ = f(XYZ / XYZn);
float X = XYZ.x;
float Y = XYZ.y;
float Z = XYZ.z;
float L = 116.0 * Y - 16.0;
float a = 500.0 * (X - Y);
float b = 200.0 * (Y - Z);
return vec3(L, a, b);
}
vec3 Lab_to_XYZ(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float Y = (L + 16.0) / 116.0;
float X = Y + a / 500.0;
float Z = Y - b / 200.0;
vec3 XYZ = f_inv(vec3(X, Y, Z)) * XYZn;
return XYZ;
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
const float epsilon = 1e-6;
vec3 Lab_to_LCh(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float C = length(vec2(a, b));
float h = (abs(a) < epsilon && abs(b) < epsilon) ? 0.0 : atan(b, a);
return vec3(L, C, h);
}
vec3 LCh_to_Lab(vec3 LCh) {
float L = LCh.x;
float C = LCh.y;
float h = LCh.z;
C = max(C, 0.0);
float a = C * cos(h);
float b = C * sin(h);
return vec3(L, a, b);
}
float chroma_correction(float L, float Lref, float Lmax, float sigma) {
return L <= Lref ? 1.0 : max(1.0 - sigma * (L - Lref) / (Lmax - Lref), 0.0);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
const float Lref = RGB_to_Lab(vec3(1.0)).x;
const float Lmax = RGB_to_Lab(vec3(1000.0 / reference_white)).x;
color.rgb = RGB_to_Lab(color.rgb);
color.rgb = Lab_to_LCh(color.rgb);
color.y *= chroma_correction(color.x, Lref, Lmax, sigma);
color.rgb = LCh_to_Lab(color.rgb);
color.rgb = Lab_to_RGB(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC tone mapping (bt.2446c)
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_xyY(vec3 XYZ) {
float X = XYZ.x;
float Y = XYZ.y;
float Z = XYZ.z;
float divisor = X + Y + Z;
if (divisor == 0.0) divisor = 1e-6;
float x = X / divisor;
float y = Y / divisor;
return vec3(x, y, Y);
}
vec3 xyY_to_XYZ(vec3 xyY) {
float x = xyY.x;
float y = xyY.y;
float Y = xyY.z;
float multiplier = Y / max(y, 1e-6);
float z = 1.0 - x - y;
float X = x * multiplier;
float Z = z * multiplier;
return vec3(X, Y, Z);
}
const float ip = 0.58535; // linear length
const float k1 = 0.83802; // linear strength
const float k3 = 0.74204; // shoulder strength
float f(float Y, float k1, float k3, float ip) {
ip /= k1;
float k2 = (k1 * ip) * (1.0 - k3);
float k4 = (k1 * ip) - (k2 * log(1.0 - k3));
return Y < ip ? Y * k1 : log((Y / ip) - k3) * k2 + k4;
}
float curve(float x) {
return f(x, k1, k3, ip);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_XYZ(color.rgb);
color.rgb = XYZ_to_xyY(color.rgb);
color.z = curve(color.z);
color.rgb = xyY_to_XYZ(color.rgb);
color.rgb = XYZ_to_RGB(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN alpha
//!DESC tone mapping (bt.2446c, inverse crosstalk)
// The inverse crosstalk matrix is applied to ensure that the original hues of input HDR images are
// recovered.
vec3 crosstalk_inv(vec3 x, float a) {
float b = 1.0 - a;
float c = 1.0 / (1.0 - 3.0 * a);
mat3 transform = mat3(
b, -a, -a,
-a, b, -a,
-a, -a, b
);
return x * transform * c;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = crosstalk_inv(color.rgb, alpha);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC tone mapping (bt.2446c, signal scaling)
// Handling 109% range (super-whites) and black point compensation
float f(float x, float a, float b, float c, float d) {
return (x - a) * (d - c) / (b - a) + c;
}
vec3 f(vec3 x, float a, float b, float c, float d) {
return vec3(
f(x.x, a, b, c, d),
f(x.y, a, b, c, d),
f(x.z, a, b, c, d)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = f(color.rgb, 0.0, 1019.0 / 940.0, 0.001, 1.0);
return color;
}

View file

@ -0,0 +1,325 @@
// Visualizes the image using false color
// You can preview the colors in Visual Studio Code by the following plugin
// https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight
//!PARAM mode
//!TYPE ENUM int
luminance
exposure
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN mode 0 =
//!DESC tone mapping (false color, luminance)
// oklch(0.99500 0.00 000.0) >10000 nits
// oklch(0.94659 0.11 005.0) 10000 nits
// oklch(0.83878 0.33 025.0) 4000 nits
// oklch(0.73097 0.33 090.0) 2000 nits
// oklch(0.52324 0.33 130.0) 1000 nits
// oklch(0.33922 0.24 245.0) brighter than SDR
// oklch(0.56925 0.00 000.0) SDR
// oklch(0.20104 0.16 350.0) darker than SDR
// oklch(0.13040 0.08 350.0) 0nits
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
vec3 cbrt(vec3 color) {
return vec3(
cbrt(color.x),
cbrt(color.y),
cbrt(color.z)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.8190224379967030, 0.3619062600528904, -0.1288737815209879,
0.0329836539323885, 0.9292868615863434, 0.0361446663506424,
0.0481771893596242, 0.2642395317527308, 0.6335478284694309
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.2268798758459243, -0.5578149944602171, 0.2813910456659647,
-0.0405757452148008, 1.1122868032803170, -0.0717110580655164,
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816
);
}
vec3 LMS_to_Lab(vec3 LMS) {
return LMS * mat3(
0.2104542683093140, 0.7936177747023054, -0.0040720430116193,
1.9779985324311684, -2.4285922420485799, 0.4505937096174110,
0.0259040424655478, 0.7827717124575296, -0.8086757549230774
);
}
vec3 Lab_to_LMS(vec3 Lab) {
return Lab * mat3(
1.0000000000000000, 0.3963377773761749, 0.2158037573099136,
1.0000000000000000, -0.1055613458156586, -0.0638541728258133,
1.0000000000000000, -0.0894841775298119, -1.2914855480194092
);
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = cbrt(color);
color = LMS_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_LMS(color);
color = pow(color, vec3(3.0));
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
const float epsilon = 1e-6;
vec3 Lab_to_LCh(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float C = length(vec2(a, b));
float h = (abs(a) < epsilon && abs(b) < epsilon) ? 0.0 : atan(b, a);
return vec3(L, C, h);
}
vec3 LCh_to_Lab(vec3 LCh) {
float L = LCh.x;
float C = LCh.y;
float h = LCh.z;
C = max(C, 0.0);
float a = C * cos(h);
float b = C * sin(h);
return vec3(L, a, b);
}
float l(float x, float a, float b) {
float y = (x - a) / (b - a);
return clamp(y , 0.0, 1.0);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
float y = RGB_to_XYZ(color.rgb).y * reference_white;
float l5 = 10000.0;
float l4 = 4000.0;
float l3 = 2000.0;
float l2 = 1000.0;
float l1 = reference_white;
float l0 = reference_white / 1000.0;
float lb = 0.0;
vec3 cw = vec3(0.99500, 0.00, radians(000.0));
vec3 c5 = vec3(0.94659, 0.11, radians(005.0));
vec3 c4 = vec3(0.83878, 0.33, radians(025.0));
vec3 c3 = vec3(0.73097, 0.33, radians(090.0));
vec3 c2 = vec3(0.52324, 0.33, radians(130.0));
vec3 c1 = vec3(0.33922, 0.24, radians(245.0));
vec3 c0 = vec3(0.20104, 0.16, radians(350.0));
vec3 cb = vec3(0.13040, 0.08, radians(350.0));
if (y > l5) color.rgb = Lab_to_RGB(LCh_to_Lab(cw));
else if (y > l4) color.rgb = Lab_to_RGB(LCh_to_Lab(mix(c4, c5, l(y, l4, l5))));
else if (y > l3) color.rgb = Lab_to_RGB(LCh_to_Lab(mix(c3, c4, l(y, l3, l4))));
else if (y > l2) color.rgb = Lab_to_RGB(LCh_to_Lab(mix(c2, c3, l(y, l2, l3))));
else if (y > l1) color.rgb = Lab_to_RGB(LCh_to_Lab(mix(c1, c2, l(y, l1, l2))));
else if (y > l0) color.rgb = vec3(l(y, l0, l1));
else color.rgb = Lab_to_RGB(LCh_to_Lab(mix(cb, c0, l(y, lb, l0))));
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN mode 1 =
//!DESC tone mapping (false color, exposure)
// Inspired by the Ansel Adams' Zone System
// https://en.wikipedia.org/wiki/Zone_System#Zones_as_tone_and_texture
// Expanded exposure stops based on the de facto devices' dynamic range
// oklch(0.99500 0.00 000.0) overexposure
// oklch(0.94659 0.11 005.0) +7 stops
// oklch(0.89269 0.22 015.0) +6 stops
// oklch(0.83878 0.33 025.0) +5 stops
// oklch(0.78487 0.11 060.0) +4 stops
// oklch(0.73097 0.33 090.0) +3 stops
// oklch(0.67706 0.22 105.0) +2 stops
// oklch(0.62315 0.11 120.0) +1 stop
// oklch(0.56925 0.00 000.0) middle gray
// oklch(0.52324 0.33 130.0) -1 stop
// oklch(0.47724 0.22 145.0) -2 stops
// oklch(0.43123 0.11 160.0) -3 stops
// oklch(0.38523 0.32 220.0) -4 stops
// oklch(0.33922 0.24 245.0) -5 stops
// oklch(0.29322 0.24 290.0) -6 stops
// oklch(0.24721 0.16 320.0) -7 stops
// oklch(0.20104 0.08 350.0) -8 stops
// oklch(0.13040 0.00 000.0) underexposure
float cbrt(float x) {
return sign(x) * pow(abs(x), 1.0 / 3.0);
}
vec3 cbrt(vec3 color) {
return vec3(
cbrt(color.x),
cbrt(color.y),
cbrt(color.z)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.8190224379967030, 0.3619062600528904, -0.1288737815209879,
0.0329836539323885, 0.9292868615863434, 0.0361446663506424,
0.0481771893596242, 0.2642395317527308, 0.6335478284694309
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
1.2268798758459243, -0.5578149944602171, 0.2813910456659647,
-0.0405757452148008, 1.1122868032803170, -0.0717110580655164,
-0.0763729366746601, -0.4214933324022432, 1.5869240198367816
);
}
vec3 LMS_to_Lab(vec3 LMS) {
return LMS * mat3(
0.2104542683093140, 0.7936177747023054, -0.0040720430116193,
1.9779985324311684, -2.4285922420485799, 0.4505937096174110,
0.0259040424655478, 0.7827717124575296, -0.8086757549230774
);
}
vec3 Lab_to_LMS(vec3 Lab) {
return Lab * mat3(
1.0000000000000000, 0.3963377773761749, 0.2158037573099136,
1.0000000000000000, -0.1055613458156586, -0.0638541728258133,
1.0000000000000000, -0.0894841775298119, -1.2914855480194092
);
}
vec3 RGB_to_Lab(vec3 color) {
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = cbrt(color);
color = LMS_to_Lab(color);
return color;
}
vec3 Lab_to_RGB(vec3 color) {
color = Lab_to_LMS(color);
color = pow(color, vec3(3.0));
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
return color;
}
const float epsilon = 1e-6;
vec3 Lab_to_LCh(vec3 Lab) {
float L = Lab.x;
float a = Lab.y;
float b = Lab.z;
float C = length(vec2(a, b));
float h = (abs(a) < epsilon && abs(b) < epsilon) ? 0.0 : atan(b, a);
return vec3(L, C, h);
}
vec3 LCh_to_Lab(vec3 LCh) {
float L = LCh.x;
float C = LCh.y;
float h = LCh.z;
C = max(C, 0.0);
float a = C * cos(h);
float b = C * sin(h);
return vec3(L, a, b);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
float stops = log2(max(RGB_to_XYZ(color.rgb).y, 1e-6) / 0.18);
if (stops >= 7.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.99500, 0.00, radians(000.0))));
else if (stops >= 6.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.94659, 0.11, radians(005.0))));
else if (stops >= 5.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.89269, 0.22, radians(015.0))));
else if (stops >= 4.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.83878, 0.33, radians(025.0))));
else if (stops >= 3.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.78487, 0.11, radians(060.0))));
else if (stops >= 2.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.73097, 0.33, radians(090.0))));
else if (stops >= 1.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.67706, 0.22, radians(105.0))));
else if (stops >= 0.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.62315, 0.11, radians(120.0))));
else if (stops >= -0.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.56925, 0.00, radians(000.0))));
else if (stops >= -1.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.52324, 0.33, radians(130.0))));
else if (stops >= -2.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.47724, 0.22, radians(145.0))));
else if (stops >= -3.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.43123, 0.11, radians(160.0))));
else if (stops >= -4.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.38523, 0.32, radians(220.0))));
else if (stops >= -5.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.33922, 0.24, radians(245.0))));
else if (stops >= -6.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.29322, 0.24, radians(290.0))));
else if (stops >= -7.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.24721, 0.16, radians(320.0))));
else if (stops >= -8.5) color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.20104, 0.08, radians(350.0))));
else color.rgb = Lab_to_RGB(LCh_to_Lab(vec3(0.13040, 0.00, radians(000.0))));
return color;
}

View file

@ -0,0 +1,203 @@
// Linear applies a simple scaling to the I component
//!PARAM min_luma
//!TYPE float
0.0
//!PARAM max_luma
//!TYPE float
0.0
//!PARAM max_cll
//!TYPE float
0.0
//!PARAM scene_max_r
//!TYPE float
0.0
//!PARAM scene_max_g
//!TYPE float
0.0
//!PARAM scene_max_b
//!TYPE float
0.0
//!PARAM max_pq_y
//!TYPE float
0.0
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!PARAM chroma_correction_scaling
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
1.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC tone mapping (linear)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.0 , 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.25336628137366,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.3592832590121217, 0.6976051147779502, -0.0358915932320290,
-0.1920808463704993, 1.1004767970374321, 0.0753748658519118,
0.0070797844607479, 0.0748396662186362, 0.8433265453898765
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
2.0701522183894223, -1.3263473389671563, 0.2066510476294053,
0.3647385209748072, 0.6805660249472273, -0.0453045459220347,
-0.0497472075358123, -0.0492609666966131, 1.1880659249923042
);
}
vec3 LMS_to_ICtCp(vec3 LMS) {
return LMS * mat3(
2048.0 / 4096.0, 2048.0 / 4096.0, 0.0 / 4096.0,
6610.0 / 4096.0, -13613.0 / 4096.0, 7003.0 / 4096.0,
17933.0 / 4096.0, -17390.0 / 4096.0, -543.0 / 4096.0
);
}
vec3 ICtCp_to_LMS(vec3 ICtCp) {
return ICtCp * mat3(
1.0, 0.0086090370379328, 0.1110296250030260,
1.0, -0.0086090370379328, -0.1110296250030260,
1.0, 0.5600313357106791, -0.3206271749873189
);
}
vec3 RGB_to_ICtCp(vec3 color) {
color *= reference_white;
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = pq_eotf_inv(color);
color = LMS_to_ICtCp(color);
return color;
}
vec3 ICtCp_to_RGB(vec3 color) {
color = ICtCp_to_LMS(color);
color = pq_eotf(color);
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
color /= reference_white;
return color;
}
float get_max_i() {
if (max_pq_y > 0.0)
return max_pq_y;
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return pq_eotf_inv(RGB_to_XYZ(scene_max_rgb).y);
}
if (max_cll > 0.0)
return pq_eotf_inv(max_cll);
if (max_luma > 0.0)
return pq_eotf_inv(max_luma);
return pq_eotf_inv(1000.0);
}
float get_min_i() {
if (min_luma > 0.0)
return pq_eotf_inv(min_luma);
return pq_eotf_inv(0.001);
}
float f(float x, float a, float b, float c, float d) {
return (x - a) * (d - c) / (b - a) + c;
}
float curve(float x) {
float ow = pq_eotf_inv(reference_white);
float ob = pq_eotf_inv(reference_white / 1000.0);
float iw = max(get_max_i(), ow + 1e-3);
float ib = min(get_min_i(), ob - 1e-3);
return f(x, ib, iw, ob, ow);
}
vec2 chroma_correction(vec2 ab, float i1, float i2) {
float r1 = i1 / max(i2, 1e-6);
float r2 = i2 / max(i1, 1e-6);
return ab * mix(1.0, min(r1, r2), chroma_correction_scaling);
}
vec3 tone_mapping(vec3 iab) {
float i2 = curve(iab.x);
vec2 ab2 = chroma_correction(iab.yz, iab.x, i2);
return vec3(i2, ab2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_ICtCp(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = ICtCp_to_RGB(color.rgb);
return color;
}

View file

@ -0,0 +1,229 @@
// Photographic tone reproduction for digital images
// https://doi.org/10.1145/566654.566575
//!PARAM max_luma
//!TYPE float
0.0
//!PARAM max_cll
//!TYPE float
0.0
//!PARAM scene_max_r
//!TYPE float
0.0
//!PARAM scene_max_g
//!TYPE float
0.0
//!PARAM scene_max_b
//!TYPE float
0.0
//!PARAM max_pq_y
//!TYPE float
0.0
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!PARAM chroma_correction_scaling
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
1.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC tone mapping (reinhard)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.0 , 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.25336628137366,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.3592832590121217, 0.6976051147779502, -0.0358915932320290,
-0.1920808463704993, 1.1004767970374321, 0.0753748658519118,
0.0070797844607479, 0.0748396662186362, 0.8433265453898765
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
2.0701522183894223, -1.3263473389671563, 0.2066510476294053,
0.3647385209748072, 0.6805660249472273, -0.0453045459220347,
-0.0497472075358123, -0.0492609666966131, 1.1880659249923042
);
}
vec3 LMS_to_ICtCp(vec3 LMS) {
return LMS * mat3(
2048.0 / 4096.0, 2048.0 / 4096.0, 0.0 / 4096.0,
6610.0 / 4096.0, -13613.0 / 4096.0, 7003.0 / 4096.0,
17933.0 / 4096.0, -17390.0 / 4096.0, -543.0 / 4096.0
);
}
vec3 ICtCp_to_LMS(vec3 ICtCp) {
return ICtCp * mat3(
1.0, 0.0086090370379328, 0.1110296250030260,
1.0, -0.0086090370379328, -0.1110296250030260,
1.0, 0.5600313357106791, -0.3206271749873189
);
}
vec3 RGB_to_ICtCp(vec3 color) {
color *= reference_white;
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = pq_eotf_inv(color);
color = LMS_to_ICtCp(color);
return color;
}
vec3 ICtCp_to_RGB(vec3 color) {
color = ICtCp_to_LMS(color);
color = pq_eotf(color);
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
color /= reference_white;
return color;
}
float get_max_l() {
if (max_pq_y > 0.0)
return pq_eotf(max_pq_y);
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return RGB_to_XYZ(scene_max_rgb).y;
}
if (max_cll > 0.0)
return max_cll;
if (max_luma > 0.0)
return max_luma;
return 1000.0;
}
float f(float x, float w) {
float simple = x / (1.0 + x);
float extended = simple * (1.0 + x / (w * w));
return extended;
}
float curve(float x) {
float w = get_max_l();
return f(x, w);
}
vec2 chroma_correction(vec2 ab, float i1, float i2) {
float r1 = i1 / max(i2, 1e-6);
float r2 = i2 / max(i1, 1e-6);
return ab * mix(1.0, min(r1, r2), chroma_correction_scaling);
}
vec3 tone_mapping(vec3 iab) {
float i2 = pq_eotf_inv(curve(pq_eotf(iab.x) / reference_white) * reference_white);
vec2 ab2 = chroma_correction(iab.yz, iab.x, i2);
return vec3(i2, ab2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_ICtCp(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = ICtCp_to_RGB(color.rgb);
return color;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC black point compensation
// https://www.color.org/WP40-Black_Point_Compensation_2010-07-27.pdf
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.000000000000000, 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.253366281373660,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 black_point_compensation(vec3 XYZ, float s, float d) {
float r = (1.0 - d) / (1.0 - s);
return r * XYZ + (1.0 - r) * RGB_to_XYZ(vec3(1.0));
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_XYZ(color.rgb);
color.rgb = black_point_compensation(color.rgb, 0.0, 0.001);
color.rgb = XYZ_to_RGB(color.rgb);
return color;
}

View file

@ -0,0 +1,431 @@
// ST 2094-10:2021 - SMPTE Standard - Dynamic Metadata for Color Volume Transform - Application #1
// https://ieeexplore.ieee.org/document/9405553
//!PARAM min_luma
//!TYPE float
0.0
//!PARAM max_luma
//!TYPE float
0.0
//!PARAM max_cll
//!TYPE float
0.0
//!PARAM max_fall
//!TYPE float
0.0
//!PARAM scene_max_r
//!TYPE float
0.0
//!PARAM scene_max_g
//!TYPE float
0.0
//!PARAM scene_max_b
//!TYPE float
0.0
//!PARAM scene_avg
//!TYPE float
0.0
//!PARAM max_pq_y
//!TYPE float
0.0
//!PARAM avg_pq_y
//!TYPE float
0.0
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!PARAM chroma_correction_scaling
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1.0
0.5
//!BUFFER METERED
//!VAR float metered_avg_l
//!STORAGE
//!HOOK OUTPUT
//!BIND HOOKED
//!SAVE AVG
//!COMPONENTS 1
//!WIDTH 1024
//!HEIGHT 1024
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 1024)
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
float l = dot(color.rgb * reference_white, y_coef);
float i = pq_eotf_inv(l);
return vec4(i, vec3(0.0));
}
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 512)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 256)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 128)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 64)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 32)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 16)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 8)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 4)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!SAVE AVG
//!WIDTH AVG.w 2 /
//!HEIGHT AVG.h 2 /
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 2)
vec4 hook() { return AVG_tex(AVG_pos); }
//!HOOK OUTPUT
//!BIND AVG
//!BIND METERED
//!SAVE AVG
//!WIDTH 1
//!HEIGHT 1
//!COMPUTE 1 1
//!WHEN avg_pq_y 0 = scene_avg 0 = *
//!DESC tone mapping (st2094-10, average, 1)
void hook() {
metered_avg_l = AVG_tex(AVG_pos).x;
}
//!HOOK OUTPUT
//!BIND HOOKED
//!BIND METERED
//!DESC tone mapping (st2094-10)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
vec3 RGB_to_XYZ(vec3 RGB) {
return RGB * mat3(
0.6369580483012914, 0.14461690358620832, 0.1688809751641721,
0.2627002120112671, 0.6779980715188708, 0.05930171646986196,
0.0 , 0.028072693049087428, 1.060985057710791
);
}
vec3 XYZ_to_RGB(vec3 XYZ) {
return XYZ * mat3(
1.716651187971268, -0.355670783776392, -0.25336628137366,
-0.666684351832489, 1.616481236634939, 0.0157685458139111,
0.017639857445311, -0.042770613257809, 0.942103121235474
);
}
vec3 XYZ_to_LMS(vec3 XYZ) {
return XYZ * mat3(
0.3592832590121217, 0.6976051147779502, -0.0358915932320290,
-0.1920808463704993, 1.1004767970374321, 0.0753748658519118,
0.0070797844607479, 0.0748396662186362, 0.8433265453898765
);
}
vec3 LMS_to_XYZ(vec3 LMS) {
return LMS * mat3(
2.0701522183894223, -1.3263473389671563, 0.2066510476294053,
0.3647385209748072, 0.6805660249472273, -0.0453045459220347,
-0.0497472075358123, -0.0492609666966131, 1.1880659249923042
);
}
vec3 LMS_to_ICtCp(vec3 LMS) {
return LMS * mat3(
2048.0 / 4096.0, 2048.0 / 4096.0, 0.0 / 4096.0,
6610.0 / 4096.0, -13613.0 / 4096.0, 7003.0 / 4096.0,
17933.0 / 4096.0, -17390.0 / 4096.0, -543.0 / 4096.0
);
}
vec3 ICtCp_to_LMS(vec3 ICtCp) {
return ICtCp * mat3(
1.0, 0.0086090370379328, 0.1110296250030260,
1.0, -0.0086090370379328, -0.1110296250030260,
1.0, 0.5600313357106791, -0.3206271749873189
);
}
vec3 RGB_to_ICtCp(vec3 color) {
color *= reference_white;
color = RGB_to_XYZ(color);
color = XYZ_to_LMS(color);
color = pq_eotf_inv(color);
color = LMS_to_ICtCp(color);
return color;
}
vec3 ICtCp_to_RGB(vec3 color) {
color = ICtCp_to_LMS(color);
color = pq_eotf(color);
color = LMS_to_XYZ(color);
color = XYZ_to_RGB(color);
color /= reference_white;
return color;
}
float get_max_l() {
if (max_pq_y > 0.0)
return pq_eotf(max_pq_y);
if (scene_max_r > 0.0 || scene_max_g > 0.0 || scene_max_b > 0.0) {
vec3 scene_max_rgb = vec3(scene_max_r, scene_max_g, scene_max_b);
return RGB_to_XYZ(scene_max_rgb).y;
}
if (max_cll > 0.0)
return max_cll;
if (max_luma > 0.0)
return max_luma;
return 1000.0;
}
float get_min_l() {
if (min_luma > 0.0)
return min_luma;
return 0.001;
}
float get_avg_l() {
if (avg_pq_y > 0.0)
return pq_eotf(avg_pq_y);
if (scene_avg > 0.0)
return scene_avg;
if (metered_avg_l > 0.0)
return pq_eotf(clamp(metered_avg_l, 0.1, 0.5));
if (max_fall > 0.0)
return max_fall;
return pq_eotf(0.3);
}
// n: contrast [0.5, 1.5]
// o: offset [-0.5, 0.5]
// g: gain [0.5, 1.5]
// p: gamma [0.5, 1.5]
float f(
float x,
float iw, float ib, float ow, float ob, float adapt,
float n, float o, float g, float p
) {
float x1 = ib;
float y1 = ob;
float x3 = iw;
float y3 = ow;
float x2 = adapt;
float y2 = sqrt(x2 * sqrt(y3 * y1));
x2 = clamp(x2, x1 + 0.001, 0.9 * x3);
y2 = clamp(y2, y1 + 0.001, 0.9 * y3);
float a = x3 * y3 * (x1 - x2) + x2 * y2 * (x3 - x1) + x1 * y1 * (x2 - x3);
mat3 cmat = mat3(
x2 * x3 * (y2 - y3), x1 * x3 * (y3 - y1), x1 * x2 * (y1 - y2),
x3 * y3 - x2 * y2 , x1 * y1 - x3 * y3 , x2 * y2 - x1 * y1 ,
x3 - x2 , x1 - x3 , x2 - x1
);
vec3 coeffs = vec3(y1, y2, y3) * cmat / a;
float c1 = coeffs.r;
float c2 = coeffs.g;
float c3 = coeffs.b;
x = clamp(x, x1, x3);
x = pow(x, n);
x = (c1 + c2 * x) / (1.0 + c3 * x);
x = pow(min(max(0.0, ((x / y3) * g) + o), 1.0), p) * y3;
x = clamp(x, y1, y3);
return x;
}
float f(float x, float iw, float ib, float ow, float ob, float adapt) {
return f(x, iw, ib, ow, ob, adapt, 1.0, 0.0, 1.0, 1.0);
}
float curve(float x) {
float ow = 1.0;
float ob = 0.001;
float iw = max(get_max_l() / reference_white, ow + 1e-3);
float ib = min(get_min_l() / reference_white, ob - 1e-3);
float avg = get_avg_l() / reference_white;
return f(x, iw, ib, ow, ob, avg);
}
vec2 chroma_correction(vec2 ab, float i1, float i2) {
float r1 = i1 / max(i2, 1e-6);
float r2 = i2 / max(i1, 1e-6);
return ab * mix(1.0, min(r1, r2), chroma_correction_scaling);
}
vec3 tone_mapping(vec3 iab) {
float i2 = pq_eotf_inv(curve(pq_eotf(iab.x) / reference_white) * reference_white);
vec2 ab2 = chroma_correction(iab.yz, iab.x, i2);
return vec3(i2, ab2);
}
// c: chroma compensation weight [-0.5, 0.5]
// s: saturation gain [-0.5, 0.5]
vec3 gamut_adjustment(vec3 f, float c, float s) {
float y = RGB_to_XYZ(f).y;
return f * pow((1.0 + c) * f / y, vec3(s));
}
vec3 gamut_adjustment(vec3 f) {
return gamut_adjustment(f, 0.0, 0.0);
}
// t: tone detail factor [0, 1];
vec3 detail_managenment(vec3 p, float t) {
// TODO: do what?
vec3 q = p;
return p * (1.0 - t) + q * t;
}
vec3 detail_managenment(vec3 p) {
return detail_managenment(p, 0.0);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = RGB_to_ICtCp(color.rgb);
color.rgb = tone_mapping(color.rgb);
color.rgb = ICtCp_to_RGB(color.rgb);
color.rgb = gamut_adjustment(color.rgb);
color.rgb = detail_managenment(color.rgb);
return color;
}

View file

@ -0,0 +1,28 @@
// https://www.itu.int/rec/R-REC-BT.1886
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (bt.1886)
float bt1886_eotf_inv(float L, float gamma, float Lw, float Lb) {
float a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
float b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
float V = pow(max(L / a, 0.0), 1.0 / gamma) - b;
return V;
}
vec3 bt1886_eotf_inv(vec3 color, float gamma, float Lw, float Lb) {
return vec3(
bt1886_eotf_inv(color.r, gamma, Lw, Lb),
bt1886_eotf_inv(color.g, gamma, Lw, Lb),
bt1886_eotf_inv(color.b, gamma, Lw, Lb)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = bt1886_eotf_inv(color.rgb, 2.4, 1.0, 0.001);
return color;
}

View file

@ -0,0 +1,28 @@
// https://www.itu.int/rec/R-REC-BT.1886
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (bt.1886, inverse)
float bt1886_eotf(float V, float gamma, float Lw, float Lb) {
float a = pow(pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma), gamma);
float b = pow(Lb, 1.0 / gamma) / (pow(Lw, 1.0 / gamma) - pow(Lb, 1.0 / gamma));
float L = a * pow(max(V + b, 0.0), gamma);
return L;
}
vec3 bt1886_eotf(vec3 color, float gamma, float Lw, float Lb) {
return vec3(
bt1886_eotf(color.r, gamma, Lw, Lb),
bt1886_eotf(color.g, gamma, Lw, Lb),
bt1886_eotf(color.b, gamma, Lw, Lb)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = bt1886_eotf(color.rgb, 2.4, 1.0, 0.001);
return color;
}

View file

@ -0,0 +1,30 @@
// https://www.itu.int/rec/R-REC-BT.601
// https://www.itu.int/rec/R-REC-BT.709
// https://www.itu.int/rec/R-REC-BT.2020
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (bt.709)
const float beta = 0.018053968510807;
const float alpha = 1.0 + 5.5 * beta;
float bt709_oetf(float L) {
return L < beta ? 4.5 * L : alpha * pow(L, 0.45) - (alpha - 1.0);
}
vec3 bt709_oetf(vec3 color) {
return vec3(
bt709_oetf(color.r),
bt709_oetf(color.g),
bt709_oetf(color.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = bt709_oetf(color.rgb);
return color;
}

View file

@ -0,0 +1,30 @@
// https://www.itu.int/rec/R-REC-BT.601
// https://www.itu.int/rec/R-REC-BT.709
// https://www.itu.int/rec/R-REC-BT.2020
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (bt.709, inverse)
const float beta = 0.018053968510807;
const float alpha = 1.0 + 5.5 * beta;
float bt709_oetf_inv(float V) {
return V < 4.5 * beta ? V / 4.5 : pow((V + (alpha - 1.0)) / alpha, 1.0 / 0.45);
}
vec3 bt709_oetf_inv(vec3 color) {
return vec3(
bt709_oetf_inv(color.r),
bt709_oetf_inv(color.g),
bt709_oetf_inv(color.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = bt709_oetf_inv(color.rgb);
return color;
}

View file

@ -0,0 +1,60 @@
// https://www.arib.or.jp/kikaku/kikaku_hoso/std-b67.html
// https://www.bbc.co.uk/rd/projects/high-dynamic-range
// https://www.itu.int/rec/R-REC-BT.2100
// https://www.itu.int/pub/R-REP-BT.2390
// extended gamma model for Lw is outside 400-2000 cd/m²:
// 1.2 * pow(1.111, log2(Lw / 1000.0)) * pow(0.98, log2(Lamb / 5.0))
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (hlg)
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
const float Lw = 1000.0;
const float Lb = 0.0;
const float Lamb = 5.0;
const float gamma = 1.2 + 0.42 * log(Lw / 1000.0) / log(10.0) - 0.076 * log(Lamb / 5.0) / log(10.0);
const float alpha = Lw;
const float beta = sqrt(3.0 * pow((Lb / Lw), 1.0 / gamma));
const float a = 0.17883277;
const float b = 1.0 - 4.0 * a;
const float c = 0.5 - a * log(4.0 * a);
float hlg_oetf(float x) {
return x <= 1.0 / 12.0 ? sqrt(3.0 * x) : a * log(12.0 * x - b) + c;
}
vec3 hlg_oetf(vec3 color) {
return vec3(
hlg_oetf(color.r),
hlg_oetf(color.g),
hlg_oetf(color.b)
);
}
vec3 hlg_ootf_inv(vec3 color) {
float Y = dot(color, y_coef);
return Y == 0.0 ? vec3(0.0) : pow(Y / alpha, (1.0 - gamma) / gamma) * (color / alpha);
}
vec3 hlg_eotf_inv(vec3 color) {
return (hlg_oetf(hlg_ootf_inv(color)) - beta) / (1.0 - beta);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = hlg_eotf_inv(color.rgb * reference_white);
return color;
}

View file

@ -0,0 +1,56 @@
// https://www.arib.or.jp/kikaku/kikaku_hoso/std-b67.html
// https://www.bbc.co.uk/rd/projects/high-dynamic-range
// https://www.itu.int/rec/R-REC-BT.2100
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (hlg, inverse)
const vec3 y_coef = vec3(0.2627002120112671, 0.6779980715188708, 0.05930171646986196);
const float Lw = 1000.0;
const float Lb = 0.0;
const float Lamb = 5.0;
const float gamma = 1.2 + 0.42 * log(Lw / 1000.0) / log(10.0) - 0.076 * log(Lamb / 5.0) / log(10.0);
const float alpha = Lw;
const float beta = sqrt(3.0 * pow((Lb / Lw), 1.0 / gamma));
const float a = 0.17883277;
const float b = 1.0 - 4.0 * a;
const float c = 0.5 - a * log(4.0 * a);
float hlg_oetf_inv(float x) {
return x <= 1.0 / 2.0 ? pow(x, 2.0) / 3.0 : (exp((x - c) / a) + b) / 12.0;
}
vec3 hlg_oetf_inv(vec3 color) {
return vec3(
hlg_oetf_inv(color.r),
hlg_oetf_inv(color.g),
hlg_oetf_inv(color.b)
);
}
vec3 hlg_ootf(vec3 color) {
float Y = dot(color, y_coef);
return alpha * pow(Y, gamma - 1.0) * color;
}
vec3 hlg_eotf(vec3 color) {
return hlg_ootf(hlg_oetf_inv(max((1.0 - beta) * color + beta, 0.0)));
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = hlg_eotf(color.rgb) / reference_white;
return color;
}

View file

@ -0,0 +1,43 @@
// https://ieeexplore.ieee.org/document/7291452
// https://www.itu.int/rec/R-REC-BT.2100
// https://www.itu.int/pub/R-REP-BT.2390
// pq ootf: 100.0 * bt1886_eotf(bt709_oetf(59.5208 * x), 2.4, 1.0, 0.0)
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (pq)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf_inv(float x) {
float t = pow(x / pw, m1);
return pow((c1 + c2 * t) / (1.0 + c3 * t), m2);
}
vec3 pq_eotf_inv(vec3 color) {
return vec3(
pq_eotf_inv(color.r),
pq_eotf_inv(color.g),
pq_eotf_inv(color.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = pq_eotf_inv(color.rgb * reference_white);
return color;
}

View file

@ -0,0 +1,40 @@
// https://ieeexplore.ieee.org/document/7291452
// https://www.itu.int/rec/R-REC-BT.2100
//!PARAM reference_white
//!TYPE float
//!MINIMUM 0.0
//!MAXIMUM 1000.0
203.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (pq, inverse)
const float m1 = 2610.0 / 4096.0 / 4.0;
const float m2 = 2523.0 / 4096.0 * 128.0;
const float c1 = 3424.0 / 4096.0;
const float c2 = 2413.0 / 4096.0 * 32.0;
const float c3 = 2392.0 / 4096.0 * 32.0;
const float pw = 10000.0;
float pq_eotf(float x) {
float t = pow(x, 1.0 / m2);
return pow(max(t - c1, 0.0) / (c2 - c3 * t), 1.0 / m1) * pw;
}
vec3 pq_eotf(vec3 color) {
return vec3(
pq_eotf(color.r),
pq_eotf(color.g),
pq_eotf(color.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = pq_eotf(color.rgb) / reference_white;
return color;
}

View file

@ -0,0 +1,31 @@
// https://github.com/ampas/aces-core/blob/dev/lib/Lib.Academy.DisplayEncoding.ctl
// moncurve with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (srgb)
const float gamma = 2.4;
const float offset = 0.055;
float monitor_curve_eotf_inv(float y) {
const float yb = pow(offset * gamma / ((gamma - 1.0) * (1.0 + offset)), gamma);
const float rs = pow((gamma - 1.0) / offset, gamma - 1.0) * pow((1.0 + offset) / gamma, gamma);
return y >= yb ? (1.0 + offset) * pow(y, 1.0 / gamma) - offset : y * rs;
}
vec3 monitor_curve_eotf_inv(vec3 color) {
return vec3(
monitor_curve_eotf_inv(color.r),
monitor_curve_eotf_inv(color.g),
monitor_curve_eotf_inv(color.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = monitor_curve_eotf_inv(color.rgb);
return color;
}

View file

@ -0,0 +1,31 @@
// https://github.com/ampas/aces-core/blob/dev/lib/Lib.Academy.DisplayEncoding.ctl
// moncurve with gamma of 2.4 and offset of 0.055 matches the EOTF found in IEC 61966-2-1:1999 (sRGB)
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transfer function (srgb, inverse)
const float gamma = 2.4;
const float offset = 0.055;
float monitor_curve_eotf(float x) {
const float fs = ((gamma - 1.0) / offset) * pow(offset * gamma / ((gamma - 1.0) * (1.0 + offset)), gamma);
const float xb = offset / (gamma - 1.0);
return x >= xb ? pow((x + offset) / (1.0 + offset), gamma) : x * fs;
}
vec3 monitor_curve_eotf(vec3 color) {
return vec3(
monitor_curve_eotf(color.r),
monitor_curve_eotf(color.g),
monitor_curve_eotf(color.b)
);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = monitor_curve_eotf(color.rgb);
return color;
}

View file

@ -0,0 +1,11 @@
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC clip code value (black)
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = max(color.rgb, 0.0);
return color;
}

View file

@ -0,0 +1,11 @@
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC clip code value (black, white)
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = clamp(color.rgb, 0.0, 1.0);
return color;
}

View file

@ -0,0 +1,11 @@
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC clip code value (white)
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = min(color.rgb, 1.0);
return color;
}

View file

@ -0,0 +1,83 @@
// https://anirban-karchaudhuri.medium.com/edge-detection-methods-comparison-9e4b75a9bf87
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC edge detection
#define tex HOOKED_texOff
const mat3 prewitt_x = mat3(
1.0, 0.0, -1.0,
1.0, 0.0, -1.0,
1.0, 0.0, -1.0
);
const mat3 prewitt_y = mat3(
1.0, 1.0, 1.0,
0.0, 0.0, 0.0,
-1.0, -1.0, -1.0
);
const mat3 sobel_x = mat3(
1.0, 0.0, -1.0,
2.0, 0.0, -2.0,
1.0, 0.0, -1.0
);
const mat3 sobel_y = mat3(
1.0, 2.0, 1.0,
0.0, 0.0, 0.0,
-1.0, -2.0, -1.0
);
const mat3 laplacian_p = mat3(
0.0, 1.0, 0.0,
1.0, -4.0, 1.0,
0.0, 1.0, 0.0
);
const mat3 laplacian_n = mat3(
0.0, -1.0, 0.0,
-1.0, 4.0, -1.0,
0.0, -1.0, 0.0
);
const float base = 0.0;
const uvec2 k_size = uvec2(3, 3);
const vec2 k_size_h = vec2(k_size / 2);
vec3 conv(mat3 k) {
vec3 x = vec3(base);
for (uint i = 0; i < k_size.x; i++)
for (uint j = 0; j < k_size.y; j++)
x += tex(vec2(j, i) - k_size_h).rgb * k[i][j];
return x;
}
vec3 prewitt() {
vec3 x = conv(prewitt_x);
vec3 y = conv(prewitt_y);
vec3 g = abs(sqrt(x * x + y * y));
return g;
}
vec3 sobel() {
vec3 x = conv(sobel_x);
vec3 y = conv(sobel_y);
vec3 g = abs(sqrt(x * x + y * y));
return g;
}
vec3 laplacian() {
vec3 x = conv(laplacian_p);
return x;
}
vec4 hook() {
vec4 color = vec4(vec3(0.0), 1.0);
color.rgb = laplacian();
return color;
}

View file

@ -0,0 +1,28 @@
// https://en.wikipedia.org/wiki/Exposure_value
//!PARAM exposure_value
//!TYPE float
//!MINIMUM -64
//!MAXIMUM 64
0.0
//!HOOK OUTPUT
//!BIND HOOKED
//!WHEN exposure_value
//!DESC exposure
float exposure(float x, float ev) {
return x * exp2(ev);
}
vec3 exposure(vec3 x, float ev) {
return x * exp2(ev);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = exposure(color.rgb, exposure_value);
return color;
}

View file

@ -0,0 +1,21 @@
// invert the signal
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC signal invert
float invert(float x, float w) {
return -x + w;
}
vec3 invert(vec3 x, float w) {
return -x + w;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
color.rgb = invert(color.rgb, 1.0);
return color;
}

View file

@ -0,0 +1,38 @@
// from "full" to "limited" signal range
//!PARAM black
//!TYPE float
0.0625
//!PARAM white
//!TYPE float
0.91796875
//!PARAM depth
//!TYPE float
10.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC signal range scaling
float range(float x, float w, float b) {
return x * (w - b) + b;
}
vec3 range(vec3 x, float w, float b) {
return x * (w - b) + b;
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
float l = exp2(depth);
float d = l - 1.0;
float b = l * black / d;
float w = l * white / d;
color.rgb = range(color.rgb, w, b);
return color;
}

View file

@ -0,0 +1,38 @@
// from "limited" to "full" signal range
//!PARAM black
//!TYPE float
0.0625
//!PARAM white
//!TYPE float
0.91796875
//!PARAM depth
//!TYPE float
10.0
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC signal range scaling (inverse)
float range_inv(float x, float w, float b) {
return (x - b) / (w - b);
}
vec3 range_inv(vec3 x, float w, float b) {
return (x - b) / (w - b);
}
vec4 hook() {
vec4 color = HOOKED_tex(HOOKED_pos);
float l = exp2(depth);
float d = l - 1.0;
float b = l * black / d;
float w = l * white / d;
color.rgb = range_inv(color.rgb, w, b);
return color;
}

View file

@ -0,0 +1,83 @@
// https://developer.mozilla.org/en-US/docs/Web/CSS/transform
//!PARAM scale_x
//!TYPE float
1.0
//!PARAM scale_y
//!TYPE float
1.0
//!PARAM translate_x
//!TYPE float
0.0
//!PARAM translate_y
//!TYPE float
0.0
//!PARAM skew_x
//!TYPE float
0.0
//!PARAM skew_y
//!TYPE float
0.0
//!PARAM rotate
//!TYPE float
0.0
//!PARAM background
//!TYPE ENUM int
black
border
repeat
mirror
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC transform
//!WHEN scale_x 1 - scale_y 1 - + translate_x + translate_y + skew_x + skew_y + rotate +
vec4 hook() {
vec2 pos = HOOKED_pos;
vec2 size = HOOKED_size;
vec2 align = vec2(0.5, 0.5); // center
pos -= align;
if (scale_x != 1.0 || scale_y != 1.0)
pos /= vec2(scale_x, scale_y);
if (translate_x != 0.0 || translate_y != 0.0)
pos -= vec2(translate_x, translate_y) * vec2(scale_x, scale_y);
if (skew_x != 0.0 || skew_y != 0.0)
pos = mat2(1.0, -tan(radians(skew_y)), -tan(radians(skew_x)), 1.0) * pos;
if (rotate != 0.0) {
pos *= size;
float c = length(pos);
float h = atan(pos.y, pos.x) - radians(rotate);
float a = cos(h) * c;
float b = sin(h) * c;
pos = vec2(a, b);
pos = floor(pos);
pos /= size;
}
pos += align;
if (background == black)
if (pos != clamp(pos, 0.0, 1.0))
return vec4(vec3(0.0), 1.0);
else if (background == border)
pos = clamp(pos, 0.0, 1.0);
else if (background == repeat)
pos = mod(pos, 1.0);
else if (background == mirror)
pos = abs(1.0 - abs(mod(pos, 2.0) - 1.0));
return HOOKED_tex(pos);
}