Files
flower/Install/Shader/Source/TonemapperFunction.glsl

178 lines
5.9 KiB
GLSL

#ifndef TONEMAPPER_FUNCTION_GLSL
#define TONEMAPPER_FUNCTION_GLSL
/*
** Physical based render code, develop by engineer: qiutanguu.
*/
// GT tonemapper.
/////////////////////////////////////////////////////////
float uchimuraTonemapper(float x, float P, float a, float m, float l, float c, float b) {
// Uchimura 2017, "HDR theory and practice"
// Math: https://www.desmos.com/calculator/gslcdxvipg
// Source: https://www.slideshare.net/nikuque/hdr-theory-and-practicce-jp
float l0 = ((P - m) * l) / a;
float L0 = m - m / a;
float L1 = m + (1.0 - m) / a;
float S0 = m + l0;
float S1 = m + a * l0;
float C2 = (a * P) / (P - S1);
float CP = -C2 / P;
float w0 = 1.0 - smoothstep(0.0, m, x);
float w2 = step(m + l0, x);
float w1 = 1.0 - w0 - w2;
float T = m * pow(x / m, c) + b;
float S = P - (P - S1) * exp(CP * (x - S0));
float L = m + a * (x - m);
return T * w0 + L * w1 + S * w2;
}
//////////////////////////////////////////////
//--------------------------------------------------------------------------------------
// AMD Tonemapper
//--------------------------------------------------------------------------------------
// General tonemapping operator, build 'b' term.
float ColToneB(float hdrMax, float contrast, float shoulder, float midIn, float midOut)
{
return
-((-pow(midIn, contrast) + (midOut*(pow(hdrMax, contrast * shoulder) * pow(midIn, contrast) -
pow(hdrMax, contrast) * pow(midIn, contrast*shoulder) * midOut)) /
(pow(hdrMax, contrast * shoulder) * midOut - pow(midIn, contrast * shoulder) * midOut)) /
(pow(midIn, contrast * shoulder) * midOut));
}
// General tonemapping operator, build 'c' term.
float ColToneC(float hdrMax, float contrast, float shoulder, float midIn, float midOut)
{
return (pow(hdrMax, contrast*shoulder)*pow(midIn, contrast) - pow(hdrMax, contrast)*pow(midIn, contrast*shoulder)*midOut) /
(pow(hdrMax, contrast*shoulder)*midOut - pow(midIn, contrast*shoulder)*midOut);
}
// General tonemapping operator, p := {contrast,shoulder,b,c}.
float ColTone(float x, vec4 p)
{
float z = pow(x, p.r);
return z / (pow(z, p.g)*p.b + p.a);
}
vec3 AMDTonemapper(vec3 color)
{
const float hdrMax = 25.0; // How much HDR range before clipping. HDR modes likely need this pushed up to say 25.0.
const float contrast = 2.0; // Use as a baseline to tune the amount of contrast the tonemapper has.
const float shoulder = 1.0; // Likely don't need to mess with this factor, unless matching existing tonemapper is not working well..
const float midIn = 0.18; // most games will have a {0.0 to 1.0} range for LDR so midIn should be 0.18.
const float midOut = 0.18; // Use for LDR. For HDR10 10:10:10:2 use maybe 0.18/25.0 to start. For scRGB, I forget what a good starting point is, need to re-calculate.
float b = ColToneB(hdrMax, contrast, shoulder, midIn, midOut);
float c = ColToneC(hdrMax, contrast, shoulder, midIn, midOut);
#define EPS 1e-6f
float peak = max(color.r, max(color.g, color.b));
peak = max(EPS, peak);
vec3 ratio = color / peak;
peak = ColTone(peak, vec4(contrast, shoulder, b, c) );
// then process ratio
// probably want send these pre-computed (so send over saturation/crossSaturation as a constant)
float crosstalk = 4.0; // controls amount of channel crosstalk
float saturation = contrast; // full tonal range saturation control
float crossSaturation = contrast*16.0; // crosstalk saturation
float white = 1.0;
// wrap crosstalk in transform
ratio = pow(abs(ratio), vec3(saturation / crossSaturation));
ratio = mix(ratio, vec3(white), vec3(pow(peak, crosstalk)));
ratio = pow(abs(ratio), vec3(crossSaturation));
// then apply ratio to peak
color = peak * ratio;
return color;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines" - https://gpuopen.com/wp-content/uploads/2016/03/GdcVdrLottes.pdf
float TonemapLottes(float x, float contrast)
{
const float a = 1.6 * contrast;
const float d = 0.977;
const float hdrMax = 8.0;
const float midIn = 0.18;
const float midOut = 0.267;
// Can be precomputed
const float b =
(-pow(midIn, a) + pow(hdrMax, a) * midOut) /
((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
const float c =
(pow(hdrMax, a * d) * pow(midIn, a) - pow(hdrMax, a) * pow(midIn, a * d) * midOut) /
((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
return pow(x, a) / (pow(x, a * d) * b + c);
}
vec3 TonemapLottesx3(vec3 c, float contrast)
{
return vec3(
TonemapLottes(c.x, contrast),
TonemapLottes(c.y, contrast),
TonemapLottes(c.z, contrast)
);
}
///////////////////////////////////////////////////////////////////
//== ACESFitted ===========================
// Baking Lab
// by MJP and David Neubelt
// http://mynameismjp.wordpress.com/
// All code licensed under the MIT license
//=========================================
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
const mat3 ACESInputMat = mat3
(
0.59719, 0.07600, 0.02840,
0.35458, 0.90834, 0.13383,
0.04823, 0.01566, 0.83777
);
// ODT_SAT => XYZ => D60_2_D65 => sRGB
const mat3 ACESOutputMat = mat3
(
1.60475, -0.10208, -0.00327,
-0.53108, 1.10813, -0.07276,
-0.07367, -0.00605, 1.07602
);
// ACES filmic tone map approximation
// see https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
vec3 RRTAndODTFit(vec3 color)
{
vec3 a = color * (color + 0.0245786) - 0.000090537;
vec3 b = color * (0.983729 * color + 0.4329510) + 0.238081;
return a / b;
}
// Tone mapping
vec3 toneMapACES(vec3 color)
{
color = ACESInputMat * color;
// Apply RRT and ODT
color = RRTAndODTFit(color);
color = ACESOutputMat * color;
// Clamp to [0, 1]
color = clamp(color, 0.0, 1.0);
return color;
}
#endif