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