179 lines
4.6 KiB
GLSL
179 lines
4.6 KiB
GLSL
/**
|
|
* @file postDeferredTonemap.glsl
|
|
*
|
|
* $LicenseInfo:firstyear=2024&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2024, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
/*[EXTRA_CODE_HERE]*/
|
|
|
|
out vec4 frag_color;
|
|
|
|
uniform sampler2D diffuseRect;
|
|
uniform sampler2D exposureMap;
|
|
|
|
uniform vec2 screen_res;
|
|
in vec2 vary_fragcoord;
|
|
|
|
vec3 linear_to_srgb(vec3 cl);
|
|
|
|
//===============================================================
|
|
// tone mapping taken from Khronos sample implementation
|
|
//===============================================================
|
|
|
|
// 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 tone map (faster approximation)
|
|
// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
|
|
vec3 toneMapACES_Narkowicz(vec3 color)
|
|
{
|
|
const float A = 2.51;
|
|
const float B = 0.03;
|
|
const float C = 2.43;
|
|
const float D = 0.59;
|
|
const float E = 0.14;
|
|
return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0);
|
|
}
|
|
|
|
|
|
// 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_Hill(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;
|
|
}
|
|
|
|
// Khronos Neutral tonemapping
|
|
// https://github.com/KhronosGroup/ToneMapping/tree/main
|
|
// Input color is non-negative and resides in the Linear Rec. 709 color space.
|
|
// Output color is also Linear Rec. 709, but in the [0, 1] range.
|
|
vec3 PBRNeutralToneMapping( vec3 color )
|
|
{
|
|
const float startCompression = 0.8 - 0.04;
|
|
const float desaturation = 0.15;
|
|
|
|
float x = min(color.r, min(color.g, color.b));
|
|
float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
|
|
color -= offset;
|
|
|
|
float peak = max(color.r, max(color.g, color.b));
|
|
if (peak < startCompression) return color;
|
|
|
|
const float d = 1. - startCompression;
|
|
float newPeak = 1. - d * d / (peak + d - startCompression);
|
|
color *= newPeak / peak;
|
|
|
|
float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.);
|
|
return mix(color, newPeak * vec3(1, 1, 1), g);
|
|
}
|
|
|
|
uniform float exposure;
|
|
uniform float tonemap_mix;
|
|
uniform int tonemap_type;
|
|
|
|
vec3 toneMap(vec3 color)
|
|
{
|
|
#ifndef NO_POST
|
|
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
|
|
|
|
color *= exposure * exp_scale;
|
|
|
|
vec3 clamped_color = clamp(color.rgb, vec3(0.0), vec3(1.0));
|
|
|
|
switch(tonemap_type)
|
|
{
|
|
case 0:
|
|
color = PBRNeutralToneMapping(color);
|
|
break;
|
|
case 1:
|
|
color = toneMapACES_Hill(color);
|
|
break;
|
|
}
|
|
|
|
// mix tonemapped and linear here to provide adjustment
|
|
color = mix(clamped_color, color, tonemap_mix);
|
|
#endif
|
|
|
|
return color;
|
|
}
|
|
|
|
//===============================================================
|
|
|
|
void debugExposure(inout vec3 color)
|
|
{
|
|
float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
|
|
exp_scale *= 0.5;
|
|
if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1)
|
|
{
|
|
color = vec3(1,0,0);
|
|
}
|
|
}
|
|
|
|
void main()
|
|
{
|
|
//this is the one of the rare spots where diffuseRect contains linear color values (not sRGB)
|
|
vec4 diff = texture(diffuseRect, vary_fragcoord);
|
|
|
|
#ifndef NO_POST
|
|
diff.rgb = toneMap(diff.rgb);
|
|
#else
|
|
diff.rgb = clamp(diff.rgb, vec3(0.0), vec3(1.0));
|
|
#endif
|
|
|
|
//debugExposure(diff.rgb);
|
|
frag_color = max(diff, vec4(0));
|
|
}
|
|
|