Skip to content
Snippets Groups Projects
Forked from Alchemy Viewer / Alchemy Viewer
10403 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
toneMapF.glsl 4.56 KiB
/** 
 * @file toneMapF.glsl
 *
 * $LicenseInfo:firstyear=2021&license=viewerlgpl$
 * Alchemy Viewer Source Code
 * Copyright (C) 2021, Rye Mutt<rye@alchemyviewer.org>
 * 
 * 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
 * 
 * $/LicenseInfo$
 */

/*[EXTRA_CODE_HERE]*/

#ifdef DEFINE_GL_FRAGCOLOR
out vec4 frag_color;
#else
#define frag_color gl_FragColor
#endif

VARYING vec2 vary_fragcoord;

uniform sampler2DRect diffuseRect;

uniform vec2 screen_res;
uniform float exposure;

vec3 linear_to_srgb(vec3 cl);

vec3 reinhard(vec3 x)
{
	return x/(1+x);
}

vec3 reinhard2(vec3 x) {
    const float L_white = 4.0;
    return (x * (1.0 + x / (L_white * L_white))) / (1.0 + x);
}

vec3 filmic(vec3 color)
{
	color = max(vec3(0.), color - vec3(0.004));
	color = (color * (6.2 * color + .5)) / (color * (6.2 * color + 1.7) + 0.06);
	return color;
}

vec3 unreal(vec3 x) {
    // Unreal 3, Documentation: "Color Grading"
    // Adapted to be close to Tonemap_ACES, with similar range
    // Gamma 2.2 correction is baked in, don't use with sRGB conversion!
    return x / (x + 0.155) * 1.019;
}

vec3 aces(vec3 x) {
    // Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
    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 (x * (a * x + b)) / (x * (c * x + d) + e);
}

vec3 uchimura(vec3 x, float P, float a, float m, float l, float c, float b) {
  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;

  vec3 w0 = vec3(1.0 - smoothstep(0.0, m, x));
  vec3 w2 = vec3(step(m + l0, x));
  vec3 w1 = vec3(1.0 - w0 - w2);

  vec3 T = vec3(m * pow(x / m, vec3(c)) + b);
  vec3 S = vec3(P - (P - S1) * exp(CP * (x - S0)));
  vec3 L = vec3(m + a * (x - m));

  return T * w0 + L * w1 + S * w2;
}

vec3 uchimura(vec3 x) {
  const float P = 1.0;  // max display brightness
  const float a = 1.0;  // contrast
  const float m = 0.22; // linear section start
  const float l = 0.4;  // linear section length
  const float c = 1.33; // black
  const float b = 0.0;  // pedestal

  return uchimura(x, P, a, m, l, c, b);
}

// Lottes 2016, "Advanced Techniques and Optimization of HDR Color Pipelines"
vec3 lottes(vec3 x) {
  const vec3 a = vec3(1.6);
  const vec3 d = vec3(0.977);
  const vec3 hdrMax = vec3(8.0);
  const vec3 midIn = vec3(0.18);
  const vec3 midOut = vec3(0.267);

  const vec3 b =
      (-pow(midIn, a) + pow(hdrMax, a) * midOut) /
      ((pow(hdrMax, a * d) - pow(midIn, a * d)) * midOut);
  const vec3 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);
}

void main()
{
    vec4 diff = texture2DRect(diffuseRect, vary_fragcoord);

#if TONEMAP_METHOD != 0
    // Exposure adjustment
    diff.rgb *= exposure; 
#endif

#if TONEMAP_METHOD == 0 // None, Gamma Correct Only
    #define NEEDS_GAMMA_CORRECT 1
#elif TONEMAP_METHOD == 1 // Linear 
    #define NEEDS_GAMMA_CORRECT 1
#elif TONEMAP_METHOD == 2 // Reinhard method
    #define NEEDS_GAMMA_CORRECT 1
    diff.rgb = reinhard(diff.rgb);
#elif TONEMAP_METHOD == 3 // Reinhard2 method
    #define NEEDS_GAMMA_CORRECT 1
    diff.rgb = reinhard2(diff.rgb);
#elif TONEMAP_METHOD == 4 // Filmic method
    diff.rgb = filmic(diff.rgb);
#elif TONEMAP_METHOD == 5 // Unreal method
    diff.rgb = unreal(diff.rgb);
#elif TONEMAP_METHOD == 6 // Aces method
    #define NEEDS_GAMMA_CORRECT 1
    diff.rgb = aces(diff.rgb);
#elif TONEMAP_METHOD == 7 // Uchimura's Gran Turismo method
    #define NEEDS_GAMMA_CORRECT 1
    diff.rgb = uchimura(diff.rgb);
#elif TONEMAP_METHOD == 8 // Lottes 2016
    #define NEEDS_GAMMA_CORRECT 1
    diff.rgb = lottes(diff.rgb);
#else
    #define NEEDS_GAMMA_CORRECT 1
#endif

#if NEEDS_GAMMA_CORRECT
    diff.rgb = linear_to_srgb(diff.rgb);
#endif
    frag_color = diff;
}