dotfiles/mpv/shaders/hdr-toys/tone-mapping/astra.glsl
2025-05-26 00:03:04 +10:00

936 lines
24 KiB
GLSL

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