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

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;
}