mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
11da542a0d
Used in situations where source luminance is greater than HDR nominal peak setting to avoid clipping by applying BT.2408 maxRGB EETF.
166 lines
4.8 KiB
Plaintext
166 lines
4.8 KiB
Plaintext
float srgb_linear_to_nonlinear_channel(float u)
|
|
{
|
|
return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055);
|
|
}
|
|
|
|
float3 srgb_linear_to_nonlinear(float3 v)
|
|
{
|
|
return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));
|
|
}
|
|
|
|
float srgb_nonlinear_to_linear_channel(float u)
|
|
{
|
|
return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
|
|
}
|
|
|
|
float3 srgb_nonlinear_to_linear(float3 v)
|
|
{
|
|
return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
|
|
}
|
|
|
|
float3 rec709_to_rec2020(float3 v)
|
|
{
|
|
float r = dot(v, float3(0.6274040f, 0.3292820f, 0.0433136f));
|
|
float g = dot(v, float3(0.0690970f, 0.9195400f, 0.0113612f));
|
|
float b = dot(v, float3(0.0163916f, 0.0880132f, 0.8955950f));
|
|
return float3(r, g, b);
|
|
}
|
|
|
|
float3 rec2020_to_rec709(float3 v)
|
|
{
|
|
float r = dot(v, float3(1.6604910, -0.5876411, -0.0728499));
|
|
float g = dot(v, float3(-0.1245505, 1.1328999, -0.0083494));
|
|
float b = dot(v, float3(-0.0181508, -0.1005789, 1.1187297));
|
|
return float3(r, g, b);
|
|
}
|
|
|
|
float reinhard_channel(float x)
|
|
{
|
|
return x / (x + 1.);
|
|
}
|
|
|
|
float3 reinhard(float3 rgb)
|
|
{
|
|
return float3(reinhard_channel(rgb.r), reinhard_channel(rgb.g), reinhard_channel(rgb.b));
|
|
}
|
|
|
|
float linear_to_st2084_channel(float x)
|
|
{
|
|
float c = pow(abs(x), 0.1593017578);
|
|
return pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375);
|
|
}
|
|
|
|
float3 linear_to_st2084(float3 rgb)
|
|
{
|
|
return float3(linear_to_st2084_channel(rgb.r), linear_to_st2084_channel(rgb.g), linear_to_st2084_channel(rgb.b));
|
|
}
|
|
|
|
float st2084_to_linear_channel(float u)
|
|
{
|
|
float c = pow(abs(u), 1. / 78.84375);
|
|
return pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578);
|
|
}
|
|
|
|
float3 st2084_to_linear(float3 rgb)
|
|
{
|
|
return float3(st2084_to_linear_channel(rgb.r), st2084_to_linear_channel(rgb.g), st2084_to_linear_channel(rgb.b));
|
|
}
|
|
|
|
float eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax)
|
|
{
|
|
float Lw_pq = linear_to_st2084_channel(Lw / 10000.);
|
|
float E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie
|
|
float maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq;
|
|
float KS = (1.5 * maxLum) - 0.5;
|
|
float E2 = E1;
|
|
if (E1 > KS)
|
|
{
|
|
float T = (E1 - KS) / (1. - KS);
|
|
float Tsquared = T * T;
|
|
float Tcubed = Tsquared * T;
|
|
float P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum;
|
|
E2 = P;
|
|
}
|
|
float E3 = E2;
|
|
float E4 = E3 * Lw_pq;
|
|
return E4;
|
|
}
|
|
|
|
float3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax)
|
|
{
|
|
float maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax);
|
|
float maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq);
|
|
|
|
// avoid divide-by-zero possibility
|
|
maxRGB1_linear = max(6.10352e-5, maxRGB1_linear);
|
|
|
|
rgb_linear *= maxRGB2_linear / maxRGB1_linear;
|
|
return rgb_linear;
|
|
}
|
|
|
|
float3 maxRGB_eetf_pq_to_linear(float3 rgb_pq, float Lw, float Lmax)
|
|
{
|
|
float3 rgb_linear = st2084_to_linear(rgb_pq);
|
|
float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);
|
|
float maxRGB1_pq = max(max(rgb_pq.r, rgb_pq.g), rgb_pq.b);
|
|
return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);
|
|
}
|
|
|
|
float3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax)
|
|
{
|
|
float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);
|
|
float maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear);
|
|
return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);
|
|
}
|
|
|
|
float3 st2084_to_linear_eetf(float3 rgb, float Lw, float Lmax)
|
|
{
|
|
return (Lw > Lmax) ? maxRGB_eetf_pq_to_linear(rgb, Lw, Lmax) : st2084_to_linear(rgb);
|
|
}
|
|
|
|
float linear_to_hlg_channel(float u)
|
|
{
|
|
float ln2_i = 1. / log(2.);
|
|
float m = 0.17883277 / ln2_i;
|
|
return (u <= (1. / 12.)) ? sqrt(3. * u) : ((log2((12. * u) - 0.28466892) * m) + 0.55991073);
|
|
}
|
|
|
|
float3 linear_to_hlg(float3 rgb, float Lw)
|
|
{
|
|
rgb = saturate(rgb);
|
|
|
|
if (Lw > 1000.)
|
|
{
|
|
rgb = maxRGB_eetf_linear_to_linear(rgb, Lw, 1000.);
|
|
rgb *= 10000. / Lw;
|
|
}
|
|
else
|
|
{
|
|
rgb *= 10.;
|
|
}
|
|
|
|
float Yd = dot(rgb, float3(0.2627, 0.678, 0.0593));
|
|
|
|
// pow(0., exponent) can lead to NaN, use smallest positive normal number
|
|
Yd = max(6.10352e-5, Yd);
|
|
|
|
rgb *= pow(Yd, -1. / 6.);
|
|
return float3(linear_to_hlg_channel(rgb.r), linear_to_hlg_channel(rgb.g), linear_to_hlg_channel(rgb.b));
|
|
}
|
|
|
|
float hlg_to_linear_channel(float u)
|
|
{
|
|
float ln2_i = 1. / log(2.);
|
|
float m = ln2_i / 0.17883277;
|
|
float a = -ln2_i * 0.55991073 / 0.17883277;
|
|
return (u <= 0.5) ? ((u * u) / 3.) : ((exp2(u * m + a) + 0.28466892) / 12.);
|
|
}
|
|
|
|
float3 hlg_to_linear(float3 v, float exponent)
|
|
{
|
|
float3 rgb = float3(hlg_to_linear_channel(v.r), hlg_to_linear_channel(v.g), hlg_to_linear_channel(v.b));
|
|
float Ys = dot(rgb, float3(0.2627, 0.678, 0.0593));
|
|
rgb *= pow(Ys, exponent);
|
|
return rgb;
|
|
}
|