/** * @file postDeferredGammaCorrect.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2007, 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$ */ #extension GL_ARB_texture_rectangle : enable /*[EXTRA_CODE_HERE]*/ #ifdef DEFINE_GL_FRAGCOLOR out vec4 frag_color; #else #define frag_color gl_FragColor #endif uniform sampler2D diffuseRect; uniform sampler2D exposureMap; uniform vec2 screen_res; VARYING vec2 vary_fragcoord; uniform float display_gamma; 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; } uniform float exposure; uniform float gamma; vec3 toneMap(vec3 color) { float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r; color *= exposure * exp_scale; #ifdef TONEMAP_ACES_NARKOWICZ color = toneMapACES_Narkowicz(color); #endif #ifdef TONEMAP_ACES_HILL color = toneMapACES_Hill(color); #endif #ifdef TONEMAP_ACES_HILL_EXPOSURE_BOOST // boost exposure as discussed in https://github.com/mrdoob/three.js/pull/19621 // this factor is based on the exposure correction of Krzysztof Narkowicz in his // implemetation of ACES tone mapping color *= 1.0/0.6; color = toneMapACES_Hill(color); #endif return linear_to_srgb(color); } //=============================================================== //================================= // borrowed noise from: // // By Morgan McGuire @morgan3d, http://graphicscodex.com // float hash(float n) { return fract(sin(n) * 1e4); } float hash(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); } float noise(float x) { float i = floor(x); float f = fract(x); float u = f * f * (3.0 - 2.0 * f); return mix(hash(i), hash(i + 1.0), u); } float noise(vec2 x) { vec2 i = floor(x); vec2 f = fract(x); // Four corners in 2D of a tile float a = hash(i); float b = hash(i + vec2(1.0, 0.0)); float c = hash(i + vec2(0.0, 1.0)); float d = hash(i + vec2(1.0, 1.0)); // Simple 2D lerp using smoothstep envelope between the values. // return vec3(mix(mix(a, b, smoothstep(0.0, 1.0, f.x)), // mix(c, d, smoothstep(0.0, 1.0, f.x)), // smoothstep(0.0, 1.0, f.y))); // Same code, with the clamps in smoothstep and common subexpressions // optimized away. vec2 u = f * f * (3.0 - 2.0 * f); return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; } //============================= vec3 legacyGamma(vec3 color) { color = 1. - clamp(color, vec3(0.), vec3(1.)); color = 1. - pow(color, vec3(gamma)); // s/b inverted already CPU-side return color; } void main() { //this is the one of the rare spots where diffuseRect contains linear color values (not sRGB) vec4 diff = texture2D(diffuseRect, vary_fragcoord); diff.rgb = toneMap(diff.rgb); diff.rgb = legacyGamma(diff.rgb); vec2 tc = vary_fragcoord.xy*screen_res*4.0; vec3 seed = (diff.rgb+vec3(1.0))*vec3(tc.xy, tc.x+tc.y); vec3 nz = vec3(noise(seed.rg), noise(seed.gb), noise(seed.rb)); diff.rgb += nz*0.003; //diff.rgb = nz; //float exp_sample = texture(exposureMap, vec2(0.5,0.5)).r; //diff.g = exp_sample; frag_color = diff; }