gytebot/src/main/resources/data/shader/PostProcessor.fsh.glsl

304 lines
8.7 KiB
GLSL

#version 430 core
// general luminance threshold at which antialiasing will take place
const float contrastThreshold = 0.03;
// additional relative luminance threshold at which antialiasing will take place
const float relativeThreshold = 0.06;
// gamma value
const float epsilon = 1.5; // used for tone mapping and gamma correcting DoF bokeh blur
// distance from the camera's origin to focal point
const float focusDistance = 0.4;
// // range of focus.
const float focusRange = 0.5; // Lower values mean more sharper image
// contrast of fragment
const float contrast = 0.3;
// additional brightness of fragment
const float brightness = 0.05;
// output fragment color
out vec4 fragColor;
// screen resolution in pixels
uniform vec2 resolution;
// fbo texture sampler
uniform sampler2D fboTexture; // RGB-components store pixel color, alpha holds linear world scene depth from the camera's origin
uniform int boolFilterDoF; // enabled Depth of Field
uniform int boolFilterFXAA; // enabled Fast Approximate Anti Aliasing
uniform int boolFilterGrain;// enable some graininess to add to every pixels
// normalized fragment coordinates
const vec2 fragCoords = gl_FragCoord.xy / resolution;
// texel size
const vec2 fboTexelSize = 1.0 / resolution;
#define true 1 // true is not natively defined
// FXAA implementation from: https://catlikecoding.com/unity/tutorials/advanced-rendering/fxaa/
// with some minor optimizations regarding branches and texture lookups
struct LuminanceData {
// stores information about luminance
float max, min, diff; // maximum, minimum and luminance difference between max/min
// fragment values and its neighboors
float texels[3][3];
// field names
#define lumSouthWest lumData.texels[0][0]
#define lumSouth lumData.texels[1][0]
#define lumSouthEast lumData.texels[2][0]
#define lumWest lumData.texels[0][1]
#define lumMiddle lumData.texels[1][1]
#define lumEast lumData.texels[2][1]
#define lumNorthWest lumData.texels[0][2]
#define lumNorth lumData.texels[1][2]
#define lumNorthEast lumData.texels[2][2]
};
float luminanceFromLinearRgb(in vec3 linearRgb)
{
// relative luminance coefficent factors
const vec3 luminanceFactors = vec3(0.2126729, 0.7151522, 0.0721750);
// compute luminance
return dot(linearRgb, luminanceFactors);
}
float texelLuminosity(in vec2 pixelOffset)
{
vec3 linearRgb = texture2D(fboTexture, fragCoords + pixelOffset * fboTexelSize).rgb;
return luminanceFromLinearRgb(linearRgb);
}
// clamp between [0, 1]
#define saturate(x) clamp(x, 0.0, 1.0)
// collect luminance data and fill struct
void collectLuminance(inout LuminanceData lumData)
{
// collect data
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
lumData.texels[x + 1][y + 1] = texelLuminosity(vec2(float(x), float(y) ) );
}
}
// calculate highest and lowest + difference i.e the contrast
lumData.max = max(max(max(max(lumEast, lumWest), lumSouth), lumNorth), lumMiddle);
lumData.min = min(min(min(min(lumEast, lumWest), lumSouth), lumNorth), lumMiddle);
// compute difference
lumData.diff = lumData.max - lumData.min;
}
// apply a basic tent blur filter, to average the fragments luminance
float fragBlendFactor(in LuminanceData lumData)
{
// these should matter more, so double their influence
float fac = 2.0 * (lumNorth + lumSouth + lumWest + lumEast);
fac += lumNorthWest + lumNorthEast + lumSouthWest + lumSouthEast;
fac /= 12.0; // scale down
fac = abs(fac - lumMiddle);
fac = saturate(fac / lumData.diff);
float blendFac = smoothstep(0.0, 1.0, fac);
return blendFac * blendFac;
}
// determine the edge type
bool determineEdge(in LuminanceData lumData)
{
// change in horizontal pixels
float hori =
abs(lumNorth + lumSouth - 2.0 * lumMiddle) * 2.0 +
abs(lumNorthEast + lumSouthEast - 2.0 * lumEast) +
abs(lumNorthWest + lumSouthWest - 2.0 * lumWest);
// change in vertical pixels
float veri =
abs(lumEast + lumWest - 2.0 * lumMiddle) * 2.0 +
abs(lumNorthEast + lumNorthWest - 2.0 * lumNorth) +
abs(lumSouthEast + lumSouthWest - 2.0 * lumSouth);
return hori >= veri;
}
vec3 filterFXAA()
{
// collect luminance data off current fragment
LuminanceData lumData;
collectLuminance(lumData);
vec2 st = fragCoords;
if (lumData.diff > max(contrastThreshold, relativeThreshold * lumData.max) )
{
float fac = fragBlendFactor(lumData);
bool edge = determineEdge(lumData);
float pixelStep = edge ? fboTexelSize.y : fboTexelSize.x;
float pLum = edge ? lumNorth : lumEast;
float nLum = edge ? lumSouth : lumWest;
float pGrad = abs(pLum - lumMiddle);
float nGrad = abs(nLum - lumMiddle);
pixelStep = pixelStep * sign(pGrad - nGrad);
st += edge ? vec2(0, pixelStep * fac) : vec2(pixelStep * fac, 0);
}
return texture2D(fboTexture, st).rgb;
}
// apply gamma correctio to linear RGB color
vec3 gamma(in vec3 linearRgb)
{
return pow(linearRgb, vec3(epsilon) );
}
// de-gamma linear RGB color
// degamma() is the inverse function of gamma()
vec3 degamma(in vec3 linearRgb)
{
return pow(linearRgb, vec3(1.0 / epsilon) );
}
// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio
// - uniform normalized distribution
// - (fastest?) static noise generator function (also runs at low precision)
// generate some 1D noise
// no seed required
float goldNoise(in vec2 xy)
{
const float phi = 1.61803398874989484820459; // Φ = Golden Ratio
return fract(tan(distance(xy * phi, xy) ) * xy.x) * 2.0 - 1.0;
}
// gamma corrected bokeh down sampling filter as DoF blur
vec3 bokeh(in float bokehSize)
{
// if sampling rate is low enough, clamp it to use FXAA instead of a single unfiltered texture lookup
if (bokehSize < 2.0)
{
if (boolFilterFXAA == true)
{
// if filter FXAA
return filterFXAA();
}
else
{
// if no DoF and no FXAA, do nearly nothing
return texture2D(fboTexture, fragCoords).rgb;
}
}
vec3 color = vec3(0);
const int steps = 8; // samples to take per axis {x,y}
const int stepsHalf = steps / 2;
const float stepSize = bokehSize / steps;
float samples = 0.0; // samples actually taken
for (int i = -stepsHalf; i < stepsHalf; i++) {
float u = stepSize * float(i) * fboTexelSize.x;
for (int j = -stepsHalf; j < stepsHalf; j++) {
float v = stepSize * float(j) * fboTexelSize.y;
// calculate sample effectiveness, as circle for perfect bokeh
float shape = 1.0 - step(stepsHalf, length(vec2(i, j) ) );
// if we have enough effectiveness, take a sample
if (shape > 0.5) {
// calculate offset coordinates for sample, and add some noise to break of grid patterns when using low amount of samples
float noiseX = goldNoise(vec2(i, j) + gl_FragCoord.xy + 1.0);
float noiseY = goldNoise(vec2(i, j) + gl_FragCoord.xy + 4.0);
vec2 coords = vec2(noiseX, noiseY) * fboTexelSize + fragCoords + vec2(u, v);
// gamma correct texture lookup and contribute
color += gamma(texture2D(fboTexture, coords).rgb * shape);
samples += shape; // we have taken a sample, so remember for normalization later
}
}
}
// de-gamma correct and normalize
return degamma(color / samples);
}
vec3 filterDoF()
{
// get scene depth
float depth = texture2D(fboTexture, fragCoords).a;
// Circle of Confusion
float CoC = clamp( (depth - focusDistance) / focusRange, -1, 1) * 16.0;
// compute bokeh blur
return bokeh(abs(CoC) );
}
void filmGrain(inout vec3 color)
{
float noiseRed = goldNoise(gl_FragCoord.xy + 1.0);
float noiseGreen = goldNoise(gl_FragCoord.xy + 16.0);
float noiseBlue = goldNoise(gl_FragCoord.xy + 32.0);
vec3 noise = vec3(noiseRed, noiseGreen, noiseBlue);
float intensity = 1.0 - sqrt(texelLuminosity(vec2(0) ) );
color = color + color * noise * intensity * 0.2;
}
void sampleColorTexture(inout vec3 color)
{
if (boolFilterDoF == true)
{
// apply DoF
color = filterDoF();
}
else if (boolFilterFXAA == true)
{
// if no DoF, filter FXAA
color = filterFXAA();
}
else
{
// if no DoF and no FXAA, do nearly nothing
color = texture2D(fboTexture, fragCoords).rgb;
}
}
void main()
{
vec3 color;
// get primary scene color
sampleColorTexture(color);
// adjust contrast and brightness
color = (color - 0.5) * (1.0 + contrast) + 0.5 + brightness;
// vignette
// falloff
float falloff = distance(fragCoords, vec2(0.5, 0.5) );
// adjust falloff and darken
color = color * (1.0 - falloff * falloff * falloff * 0.2);
// tone map
color = clamp(color, 0.0, 1.0);
color = 1.0 - pow(1.0 - color, vec3(epsilon) );
if (boolFilterGrain == true)
{
// add some film grain
filmGrain(color);
}
fragColor = vec4(color, 1);
}