2023-09-27 06:49:59 +00:00
|
|
|
/// tip:"Requires MSAA off for best results"
|
|
|
|
|
2023-07-30 19:18:50 +00:00
|
|
|
/*****************************************
|
|
|
|
* FXAA 3.11 Implementation - effendiian
|
|
|
|
* -------------------------------------
|
|
|
|
* FXAA implementation based off of the
|
|
|
|
* work by Timothy Lottes in the Nvidia white paper:
|
|
|
|
* https://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf
|
|
|
|
*
|
|
|
|
* Also used these resources:
|
|
|
|
* - https://catlikecoding.com/unity/tutorials/advanced-rendering/fxaa/
|
|
|
|
* - https://blog.codinghorror.com/fast-approximate-anti-aliasing-fxaa/
|
|
|
|
*****************************************/
|
|
|
|
|
|
|
|
// Turn off FXAA.
|
|
|
|
// #define FXAA 0
|
|
|
|
|
|
|
|
// Turn on FXAA.
|
|
|
|
#define FXAA 1
|
|
|
|
|
|
|
|
// Turn on split screen between no-FXAA and FXAA.
|
|
|
|
// #define FXAA 2
|
|
|
|
|
|
|
|
/*
|
|
|
|
/ FXAA setting, defined via preprocessor variables
|
|
|
|
*/
|
|
|
|
#ifndef FXAA_PRESET
|
|
|
|
#define FXAA_PRESET 5
|
|
|
|
#define FXAA_DEBUG_SKIPPED 0
|
|
|
|
#define FXAA_DEBUG_PASSTHROUGH 0
|
|
|
|
#define FXAA_DEBUG_HORZVERT 0
|
|
|
|
#define FXAA_DEBUG_PAIR 0
|
|
|
|
#define FXAA_DEBUG_NEGPOS 0
|
|
|
|
#define FXAA_DEBUG_OFFSET 0
|
|
|
|
#define FXAA_DEBUG_HIGHLIGHT 0
|
|
|
|
#define FXAA_LUMINANCE 1
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#if (FXAA_PRESET == 0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD_MIN (1.0/12.0)
|
|
|
|
#define FXAA_SEARCH_STEPS 2
|
|
|
|
#define FXAA_SEARCH_ACCELERATION 4
|
|
|
|
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_SUBPIX 1
|
|
|
|
#define FXAA_SUBPIX_FASTER 1
|
|
|
|
#define FXAA_SUBPIX_CAP (2.0/3.0)
|
|
|
|
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#if (FXAA_PRESET == 1)
|
|
|
|
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD_MIN (1.0/16.0)
|
|
|
|
#define FXAA_SEARCH_STEPS 4
|
|
|
|
#define FXAA_SEARCH_ACCELERATION 3
|
|
|
|
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_SUBPIX 1
|
|
|
|
#define FXAA_SUBPIX_FASTER 0
|
|
|
|
#define FXAA_SUBPIX_CAP (3.0/4.0)
|
|
|
|
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#if (FXAA_PRESET == 2)
|
|
|
|
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
|
|
|
|
#define FXAA_SEARCH_STEPS 8
|
|
|
|
#define FXAA_SEARCH_ACCELERATION 2
|
|
|
|
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_SUBPIX 1
|
|
|
|
#define FXAA_SUBPIX_FASTER 0
|
|
|
|
#define FXAA_SUBPIX_CAP (3.0/4.0)
|
|
|
|
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#if (FXAA_PRESET == 3)
|
|
|
|
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
|
|
|
|
#define FXAA_SEARCH_STEPS 16
|
|
|
|
#define FXAA_SEARCH_ACCELERATION 1
|
|
|
|
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_SUBPIX 1
|
|
|
|
#define FXAA_SUBPIX_FASTER 0
|
|
|
|
#define FXAA_SUBPIX_CAP (3.0/4.0)
|
|
|
|
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#if (FXAA_PRESET == 4)
|
|
|
|
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
|
|
|
|
#define FXAA_SEARCH_STEPS 24
|
|
|
|
#define FXAA_SEARCH_ACCELERATION 1
|
|
|
|
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_SUBPIX 1
|
|
|
|
#define FXAA_SUBPIX_FASTER 0
|
|
|
|
#define FXAA_SUBPIX_CAP (3.0/4.0)
|
|
|
|
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#if (FXAA_PRESET == 5)
|
|
|
|
#define FXAA_EDGE_THRESHOLD (1.0/8.0)
|
|
|
|
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0)
|
|
|
|
#define FXAA_SEARCH_STEPS 32
|
|
|
|
#define FXAA_SEARCH_ACCELERATION 1
|
|
|
|
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
|
|
#define FXAA_SUBPIX 1
|
|
|
|
#define FXAA_SUBPIX_FASTER 0
|
|
|
|
#define FXAA_SUBPIX_CAP (3.0/4.0)
|
|
|
|
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
#define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM))
|
|
|
|
|
|
|
|
// --------------------------------------
|
|
|
|
// Helper functions.
|
|
|
|
// --------------------------------------
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Conversion functions.
|
|
|
|
|
|
|
|
// ToVec2
|
|
|
|
vec2 ToVec2( float value ) { return vec2(value, value); }
|
|
|
|
|
|
|
|
// ToVec3
|
|
|
|
vec3 ToVec3( float value ) { return vec3(value, value, value); }
|
|
|
|
vec3 ToVec3( vec2 vector, float z ) { return vec3(vector.x, vector.y, z); }
|
|
|
|
vec3 ToVec3( vec2 vector ) { return ToVec3(vector, 0.0); }
|
|
|
|
|
|
|
|
// ToVec4
|
|
|
|
vec4 ToVec4( vec2 vector, float z, float w ) { return vec4(vector.x, vector.y, z, w); }
|
|
|
|
vec4 ToVec4( vec2 vector, float z ) { return ToVec4(vector, z, 0.0); }
|
|
|
|
vec4 ToVec4( vec2 vector ) { return ToVec4(vector, 0.0); }
|
|
|
|
vec4 ToVec4( vec3 vector, float w ) { return vec4(vector.x, vector.y, vector.z, w); }
|
|
|
|
vec4 ToVec4( vec3 vector ) { return ToVec4(vector, 0.0); }
|
|
|
|
vec4 ToVec4( float value, float w ) { return vec4(value, value, value, w); }
|
|
|
|
vec4 ToVec4( float value ) { return ToVec4(value, 0.0); }
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Texture sampler functions.
|
|
|
|
|
|
|
|
// Return sampled image from a point + offset texel space.
|
|
|
|
vec4 TextureOffset( sampler2D tex,
|
|
|
|
vec2 uv,
|
|
|
|
vec2 offset ) {
|
|
|
|
|
|
|
|
// Return color from the specified location.
|
|
|
|
return texture(tex, uv + offset);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Grayscale functions.
|
|
|
|
|
|
|
|
// Return grayscaled image based off of the selected color channel.
|
|
|
|
vec3 Grayscale( vec3 color, int index ) {
|
|
|
|
int selectedChannel = clamp(index, 0, 2); // [0]r, [1]g, [2]b.
|
|
|
|
return ToVec3(color[selectedChannel]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return grayscaled image based off of the selected color channel.
|
|
|
|
vec4 Grayscale( vec4 color, int index ) {
|
|
|
|
int selectedChannel = clamp(index, 0, 3); // [0]r, [1]g, [2]b, [3]a.
|
|
|
|
return ToVec4(color[selectedChannel]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default to green color channel when no index is supplied.
|
|
|
|
vec3 Grayscale( vec3 color ) { return Grayscale(color, 1); }
|
|
|
|
vec4 Grayscale( vec4 color ) { return Grayscale(color, 1); }
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Luminance functions.
|
|
|
|
|
|
|
|
// Map RGB to Luminance linearly.
|
|
|
|
float LinearRGBLuminance( vec3 color ) {
|
|
|
|
|
|
|
|
// Weights for relative luma from here: https://en.wikipedia.org/wiki/Luma_(video)
|
|
|
|
vec3 weight = vec3(0.2126729, 0.7151522, 0.0721750);
|
|
|
|
|
|
|
|
// Get the dot product:
|
|
|
|
// - color.r * weight.r + color.g * weight.g + color.b * weight*b.
|
|
|
|
return dot(color, weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Luminance based off of the original specification.
|
|
|
|
float FXAALuminance( vec3 color ) {
|
|
|
|
|
|
|
|
#if FXAA_LUMINANCE == 0
|
|
|
|
|
|
|
|
return LinearRGBLuminance( color );
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
return color.g * (0.587/0.299) + color.r;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Vertical/Horizontal Edge Test functions.
|
|
|
|
|
|
|
|
float FXAAVerticalEdge( float lumaO,
|
|
|
|
float lumaN,
|
|
|
|
float lumaE,
|
|
|
|
float lumaS,
|
|
|
|
float lumaW,
|
|
|
|
float lumaNW,
|
|
|
|
float lumaNE,
|
|
|
|
float lumaSW,
|
|
|
|
float lumaSE ) {
|
|
|
|
|
|
|
|
// Slices to calculate.
|
|
|
|
float top = (0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE);
|
|
|
|
float middle = (0.50 * lumaW ) + (-1.0 * lumaO) + (0.50 * lumaE );
|
|
|
|
float bottom = (0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE);
|
|
|
|
|
|
|
|
// Return value.
|
|
|
|
return abs(top) + abs(middle) + abs(bottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
float FXAAHorizontalEdge( float lumaO,
|
|
|
|
float lumaN,
|
|
|
|
float lumaE,
|
|
|
|
float lumaS,
|
|
|
|
float lumaW,
|
|
|
|
float lumaNW,
|
|
|
|
float lumaNE,
|
|
|
|
float lumaSW,
|
|
|
|
float lumaSE ) {
|
|
|
|
|
|
|
|
// Slices to calculate.
|
|
|
|
float top = (0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW);
|
|
|
|
float middle = (0.50 * lumaN ) + (-1.0 * lumaO) + (0.50 * lumaS );
|
|
|
|
float bottom = (0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE);
|
|
|
|
|
|
|
|
// Return value.
|
|
|
|
return abs(top) + abs(middle) + abs(bottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------
|
|
|
|
// FXAA specific functions.
|
|
|
|
// ------------------------
|
|
|
|
|
|
|
|
// Entry point for the FXAA process.
|
|
|
|
vec3 applyFXAA(sampler2D textureSource,
|
|
|
|
vec2 textureDimensions,
|
|
|
|
vec2 pixelPosition,
|
|
|
|
vec2 screenResolution) {
|
|
|
|
|
|
|
|
// Normalized pixel coordinates (from 0 to 1).
|
|
|
|
vec2 uv = pixelPosition / screenResolution;
|
|
|
|
|
|
|
|
// Calculate distance between pixels in texture space.
|
|
|
|
vec2 texel = vec2(1.0, 1.0) / textureDimensions;
|
|
|
|
|
|
|
|
// Caculate the luminance.
|
|
|
|
// float luma = FXAALuminance(rgbO.xyz);
|
|
|
|
// float luma = LinearRGBLuminance(clamp(rgbO.xyz, 0.0, 1.0));
|
|
|
|
|
|
|
|
//-------------------------
|
|
|
|
// 1. LOCAL CONTRAST CHECK
|
|
|
|
|
|
|
|
// Sample textures from cardinal directions.
|
|
|
|
vec3 rgbN = TextureOffset(textureSource, uv, vec2(0, -texel.y)).rgb; // NORTH
|
|
|
|
vec3 rgbW = TextureOffset(textureSource, uv, vec2(-texel.x, 0)).rgb; // WEST
|
|
|
|
vec3 rgbO = TextureOffset(textureSource, uv, vec2(0, 0)).rgb; // ORIGIN
|
|
|
|
vec3 rgbE = TextureOffset(textureSource, uv, vec2(texel.x, 0)).rgb; // EAST
|
|
|
|
vec3 rgbS = TextureOffset(textureSource, uv, vec2(0, texel.y)).rgb; // SOUTH
|
|
|
|
|
|
|
|
#if FXAA == 0
|
|
|
|
return rgbO; // Skip FXAA if it is off.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Calculate the luminance for each sampled value.
|
|
|
|
float lumaN = FXAALuminance(rgbN);
|
|
|
|
float lumaW = FXAALuminance(rgbW);
|
|
|
|
float lumaO = FXAALuminance(rgbO);
|
|
|
|
float lumaE = FXAALuminance(rgbE);
|
|
|
|
float lumaS = FXAALuminance(rgbS);
|
|
|
|
|
|
|
|
// Calculate the minimum luma range.
|
|
|
|
float minLuma = min( lumaO, min( min( lumaN, lumaW ), min( lumaS, lumaE ) ) );
|
|
|
|
float maxLuma = max( lumaO, max( max( lumaN, lumaW ), max( lumaS, lumaE ) ) );
|
|
|
|
float localContrast = maxLuma - minLuma;
|
|
|
|
|
|
|
|
// Check for early exit.
|
|
|
|
if(localContrast < max( FXAA_EDGE_THRESHOLD_MIN, maxLuma * FXAA_EDGE_THRESHOLD )) {
|
|
|
|
|
|
|
|
#if FXAA_DEBUG_SKIPPED
|
|
|
|
|
|
|
|
return vec3(0);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
return rgbO;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------
|
|
|
|
// 2. SUB-PIXEL ALIASING TEST
|
|
|
|
|
|
|
|
// Calculate the pixel contrast ratio.
|
|
|
|
// - Sub-pixel aliasing is detected by taking the ratio of the
|
|
|
|
// pixel contrast over the local contrast. This ratio nears 1.0
|
|
|
|
// in the presence of single pixel dots and otherwise falls off
|
|
|
|
// towards 0.0 as more pixels contribute to an edge. This ratio
|
|
|
|
// is transformed into the amount of lowpass filter to blend in
|
|
|
|
// at the end of the algorithm.
|
|
|
|
|
|
|
|
#if FXAA_SUBPIX > 0
|
|
|
|
|
|
|
|
// Calculate sum of local samples for the lowpass.
|
|
|
|
vec3 rgbL = (rgbN + rgbW + rgbO + rgbE + rgbS);
|
|
|
|
|
|
|
|
#if FXAA_SUBPIX_FASTER
|
|
|
|
|
|
|
|
// Average the lowpass now since this skips the addition of the diagonal neighbors (NW, NE, SW, SE).
|
|
|
|
rgbL *= (1.0/5.0);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Calculate the lowpass luma.
|
|
|
|
// - Lowpass luma is calculated as the average between the luma of neigboring pixels.
|
|
|
|
float lumaL = (lumaN + lumaW + lumaS + lumaE) * 0.25;
|
|
|
|
|
|
|
|
// Calculate the pixel contrast.
|
|
|
|
// - Pixel contrast is the abs() difference between origin pixel luma and lowpass luma of neighbors.
|
|
|
|
float pixelContrast = abs(lumaL - lumaO);
|
|
|
|
|
|
|
|
// Remember:
|
|
|
|
// - pixel contrast is the origin - lowpass(neighbors).
|
|
|
|
// - local contrast is the min(origin + neighbors) - max(origin + neighbors) < threshold.
|
|
|
|
|
|
|
|
// Calculate the ratio between the pixelContrast and localContrast.
|
|
|
|
float contrastRatio = pixelContrast / localContrast;
|
|
|
|
float lowpassBlend = 0.0; // Default is zero. Will be changed depending on subpixel level.
|
|
|
|
|
|
|
|
#if FXAA_SUBPIX == 1
|
|
|
|
|
|
|
|
// Normal subpixel aliasing. Set based on FXAA algorithm for subpixel aliasing.
|
|
|
|
lowpassBlend = max( 0.0, contrastRatio - FXAA_SUBPIX_TRIM ) * FXAA_SUBPIX_TRIM_SCALE;
|
|
|
|
lowpassBlend = min( FXAA_SUBPIX_CAP, lowpassBlend );
|
|
|
|
|
|
|
|
#elif FXAA_SUBPIX == 2
|
|
|
|
|
|
|
|
// Full force subpixel aliasing. Set blend to ratio.
|
|
|
|
lowpassBlend = contrastRatio;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Show selected pixels if debug mode is active.
|
|
|
|
#if FXAA_DEBUG_PASSTHROUGH
|
|
|
|
|
|
|
|
#if FXAA_SUBPIX > 0
|
|
|
|
|
|
|
|
return vec3(localContrast, lowpassBlend, 0.0);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
return vec3(localContrast, 0.0, 0.0);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//-------------------------
|
|
|
|
// 3. VERTICAL & HORIZONTAL EDGE TEST
|
|
|
|
|
|
|
|
// Sample the additional diagonal neighbors.
|
|
|
|
vec3 rgbNW = TextureOffset(textureSource, uv, vec2(-texel.x, -texel.y)).rgb; // NORTH-WEST
|
|
|
|
vec3 rgbNE = TextureOffset(textureSource, uv, vec2(texel.x, -texel.y)).rgb; // NORTH-EAST
|
|
|
|
vec3 rgbSW = TextureOffset(textureSource, uv, vec2(-texel.x, texel.y)).rgb; // SOUTH-WEST
|
|
|
|
vec3 rgbSE = TextureOffset(textureSource, uv, vec2(texel.x, texel.y)).rgb; // SOUTH-EAST
|
|
|
|
|
|
|
|
// Average additional neighbors when sub-pix aliasing is on and it isn't in 'fast' mode.
|
|
|
|
#if FXAA_SUBPIX > 0
|
|
|
|
#if FXAA_SUBPIX_FASTER == 0
|
|
|
|
// Add missing neighbors and average them.
|
|
|
|
rgbL += (rgbNW + rgbNE + rgbSW + rgbSE);
|
|
|
|
rgbL *= (1.0/9.0);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Calculate luma for additional neighbors.
|
|
|
|
float lumaNW = FXAALuminance(rgbNW);
|
|
|
|
float lumaNE = FXAALuminance(rgbNE);
|
|
|
|
float lumaSW = FXAALuminance(rgbSW);
|
|
|
|
float lumaSE = FXAALuminance(rgbSE);
|
|
|
|
|
|
|
|
// Calculate the vertical and horizontal edges. (Uses algorithm from FXAA white paper).
|
|
|
|
float edgeVert = FXAAVerticalEdge(lumaO, lumaN, lumaE, lumaS, lumaW, lumaNW, lumaNE, lumaSW, lumaSE);
|
|
|
|
float edgeHori = FXAAHorizontalEdge(lumaO, lumaN, lumaE, lumaS, lumaW, lumaNW, lumaNE, lumaSW, lumaSE);
|
|
|
|
|
|
|
|
// Check if edge is horizontal.
|
|
|
|
bool isHorizontal = edgeHori >= edgeVert;
|
|
|
|
|
|
|
|
#if FXAA_DEBUG_HORZVERT
|
|
|
|
if(isHorizontal)
|
|
|
|
{
|
|
|
|
return vec3(1.0, 0.75, 0.0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return vec3(0.10, 0.10, 1.0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//-------------------------
|
|
|
|
// 4. FIND HIGHEST CONTRAST PAIR 90deg TO EDGE
|
|
|
|
|
|
|
|
// Contain the appropriate sign for the top left.
|
|
|
|
float edgeSign = isHorizontal ? -texel.y : -texel.x; // Note, if isHorizontal == true, -texel.y is applied (not -texel.x).
|
|
|
|
|
|
|
|
// Calculate the gradients. The luma used changes based on the horizontal edge status.
|
|
|
|
float gradientNeg = isHorizontal ? abs(lumaN - lumaO) : abs(lumaW - lumaO);
|
|
|
|
float gradientPos = isHorizontal ? abs(lumaS - lumaO) : abs(lumaE - lumaO);
|
|
|
|
|
|
|
|
// Calculate the luma based on its direction.
|
|
|
|
// It is an average of the origin and the luma in the respective direction.
|
|
|
|
float lumaNeg = isHorizontal ? ((lumaN + lumaO) * 0.5) : ((lumaW + lumaO) * 0.5);
|
|
|
|
float lumaPos = isHorizontal ? ((lumaS + lumaO) * 0.5) : ((lumaE + lumaO) * 0.5);
|
|
|
|
|
|
|
|
// Select the highest gradient pair.
|
|
|
|
bool isNegative = (gradientNeg >= gradientPos);
|
|
|
|
float gradientHighest = isNegative ? gradientNeg : gradientPos; // Assign higher pair.
|
|
|
|
float lumaHighest = isNegative ? lumaNeg : lumaPos;
|
|
|
|
|
|
|
|
// If gradient pair in the negative direction is higher, flip the edge sign.
|
|
|
|
if(isNegative) { edgeSign *= -1.0; }
|
|
|
|
|
|
|
|
#if FXAA_DEBUG_PAIR
|
|
|
|
return isHorizontal ? vec3(0.0, gradientHighest, lumaHighest) : vec3(0.0, lumaHighest, gradientHighest);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//-------------------------
|
|
|
|
// 5. END-OF-EDGE SEARCH
|
|
|
|
|
|
|
|
// Select starting point.
|
|
|
|
vec2 pointN = vec2(0.0, 0.0);
|
|
|
|
pointN.x = uv.x + (isHorizontal ? 0.0 : edgeSign * 0.5);
|
|
|
|
pointN.y = uv.y + (isHorizontal ? edgeSign * 0.5 : 0.0);
|
|
|
|
|
|
|
|
// Assign search limiting values.
|
|
|
|
gradientHighest *= FXAA_SEARCH_THRESHOLD;
|
|
|
|
|
|
|
|
// Prepare variables for search.
|
|
|
|
vec2 pointP = pointN; // Start at the same point.
|
|
|
|
vec2 pointOffset = isHorizontal ? vec2(texel.x, 0.0) : vec2(0.0, texel.y);
|
|
|
|
float lumaNegEnd = lumaNeg;
|
|
|
|
float lumaPosEnd = lumaPos;
|
|
|
|
bool searchNeg = false;
|
|
|
|
bool searchPos = false;
|
|
|
|
|
|
|
|
// Apply values based on FXAA flags.
|
|
|
|
if(FXAA_SEARCH_ACCELERATION == 1) {
|
|
|
|
|
|
|
|
pointN += pointOffset * vec2(-1.0);
|
|
|
|
pointP += pointOffset * vec2(1.0);
|
|
|
|
// pointOffset *= vec2(1.0);
|
|
|
|
|
|
|
|
} else if(FXAA_SEARCH_ACCELERATION == 2) {
|
|
|
|
|
|
|
|
pointN += pointOffset * vec2(-1.5);
|
|
|
|
pointP += pointOffset * vec2(1.5);
|
|
|
|
pointOffset *= vec2(2.0);
|
|
|
|
|
|
|
|
} else if(FXAA_SEARCH_ACCELERATION == 3) {
|
|
|
|
|
|
|
|
pointN += pointOffset * vec2(-2.0);
|
|
|
|
pointP += pointOffset * vec2(2.0);
|
|
|
|
pointOffset *= vec2(3.0);
|
|
|
|
|
|
|
|
} else if(FXAA_SEARCH_ACCELERATION == 4) {
|
|
|
|
|
|
|
|
pointN += pointOffset * vec2(-2.5);
|
|
|
|
pointP += pointOffset * vec2(2.5);
|
|
|
|
pointOffset *= vec2(4.0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform the end-of-edge search.
|
|
|
|
for(int i = 0; i < FXAA_SEARCH_STEPS; i++)
|
|
|
|
{
|
|
|
|
if(FXAA_SEARCH_ACCELERATION == 1) {
|
|
|
|
if(!searchNeg) { lumaNegEnd = FXAALuminance(texture(textureSource, pointN).rgb); }
|
|
|
|
if(!searchPos) { lumaPosEnd = FXAALuminance(texture(textureSource, pointP).rgb); }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(!searchNeg) { lumaNegEnd = FXAALuminance(textureGrad(textureSource, pointN, pointOffset, pointOffset).rgb); }
|
|
|
|
if(!searchPos) { lumaPosEnd = FXAALuminance(textureGrad(textureSource, pointP, pointOffset, pointOffset).rgb); }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for significant change in luma compared to current highest pair.
|
|
|
|
#if 0 // original
|
|
|
|
searchNeg = searchNeg || (abs(lumaNegEnd - lumaNeg) >= gradientNeg);
|
|
|
|
searchPos = searchPos || (abs(lumaPosEnd - lumaPos) >= gradientPos);
|
|
|
|
#else // iradicator's fix
|
|
|
|
searchNeg = searchNeg || (abs(lumaNegEnd - lumaHighest) >= gradientHighest);
|
|
|
|
searchPos = searchPos || (abs(lumaPosEnd - lumaHighest) >= gradientHighest);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Display debug information regarding edges.
|
|
|
|
#if FXAA_DEBUG_NEGPOS
|
|
|
|
|
|
|
|
if(searchNeg) {
|
|
|
|
return vec3(abs(lumaNegEnd - gradientNeg), 0.0, 0.0);
|
|
|
|
} else if(searchPos) {
|
|
|
|
return vec3(0.0, 0.0, abs(lumaPosEnd - gradientPos));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Determine if search is over early.
|
|
|
|
if(searchNeg && searchPos) { break; }
|
|
|
|
|
|
|
|
// If still searching, increment offset.
|
|
|
|
if(!searchNeg) { pointN -= pointOffset; }
|
|
|
|
if(!searchPos) { pointP += pointOffset; }
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------
|
|
|
|
// 6. SUB-PIXEL SHIFT
|
|
|
|
|
|
|
|
// Determine if sub-pixel center falls on positive or negative side.
|
|
|
|
float distanceNeg = isHorizontal ? uv.x - pointN.x : uv.y - pointN.y;
|
|
|
|
float distancePos = isHorizontal ? pointP.x - uv.x : pointP.y - uv.y;
|
|
|
|
bool isCloserToNegative = distanceNeg < distancePos;
|
|
|
|
|
|
|
|
// Assign respective luma.
|
|
|
|
float lumaEnd = isCloserToNegative ? lumaNegEnd : lumaPosEnd;
|
|
|
|
|
|
|
|
// Check if pixel is in area that receives no filtering.
|
|
|
|
if( ((lumaO - lumaNeg) < 0.0) == ((lumaEnd - lumaNeg) < 0.0) ) {
|
|
|
|
edgeSign = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute sub-pixel offset and filter span.
|
|
|
|
float filterSpanLength = (distancePos + distanceNeg);
|
|
|
|
float filterDistance = isCloserToNegative ? distanceNeg : distancePos;
|
|
|
|
float subpixelOffset = ( 0.5 + ( filterDistance * (-1.0 / filterSpanLength) ) ) * edgeSign;
|
|
|
|
|
|
|
|
#if FXAA_DEBUG_OFFSET
|
|
|
|
|
|
|
|
if(subpixelOffset < 0.0) {
|
|
|
|
return isHorizontal ? vec3(1.0, 0.0, 0.0) : vec3(1.0, 0.7, 0.1); // neg-horizontal (red) : neg-vertical (gold)
|
|
|
|
}
|
|
|
|
|
|
|
|
if(subpixelOffset > 0.0) {
|
|
|
|
return isHorizontal ? vec3(0.0, 0.0, 1.0) : vec3(0.1, 0.3, 1.0); // pos-horizontal (blue) : pos-vertical (skyblue)
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Resample using the subpixel offset.
|
|
|
|
vec3 rgbOffset = textureLod(textureSource, vec2( uv.x + (isHorizontal ? 0.0 : subpixelOffset), uv.y + (isHorizontal ? subpixelOffset : 0.0)), 0.0).rgb;
|
|
|
|
|
|
|
|
// return vec3((lumaN + lumaS + lumaE + lumaW + lumaNW + lumaNE + lumaSW + lumaSE) * (1.0/9.0));
|
|
|
|
|
|
|
|
#if FXAA_DEBUG_HIGHLIGHT
|
|
|
|
|
|
|
|
return isHorizontal ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Return the FXAA effect.
|
|
|
|
#if FXAA_SUBPIX == 0
|
|
|
|
|
|
|
|
return vec3(rgbOffset);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
return mix(rgbOffset, rgbL, lowpassBlend);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------
|
|
|
|
// Main function.
|
|
|
|
// ------------------------
|
|
|
|
|
|
|
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|
|
|
{
|
|
|
|
|
|
|
|
#if (FXAA == 2)
|
|
|
|
|
|
|
|
vec2 uv = fragCoord/iResolution.xy; // Normalized pixel coordinates (from 0 to 1)
|
|
|
|
vec3 resultFXAA = vec3(1.0);
|
|
|
|
|
|
|
|
float speed = 0.45;
|
|
|
|
vec2 extents = vec2(0.1, 0.8);
|
|
|
|
float divisor = ( ((sin(iTime * speed) * 0.5) + 0.5) * extents.y ) + extents.x;
|
|
|
|
float increment = 0.005;
|
|
|
|
|
|
|
|
float divNeg = divisor - increment;
|
|
|
|
float divPos = divisor + increment;
|
|
|
|
|
|
|
|
if(uv.x >= divNeg && uv.x <= divPos) { resultFXAA = vec3(0.1); }
|
|
|
|
if(uv.x < divNeg) { resultFXAA = mix(texture(iChannel0, vec2(uv.x, uv.y)).xyz, vec3(0.9, 0.9, 0.9), 0.1); }
|
|
|
|
if(uv.x > divPos) { resultFXAA = applyFXAA(iChannel0, iChannelResolution[0].xy, fragCoord, iResolution.xy); }
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
// Calculuate the FXAA value for the whole screen.
|
|
|
|
vec3 resultFXAA = applyFXAA(iChannel0, iChannelResolution[0].xy, fragCoord, iResolution.xy);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Return the sampled pixel.
|
|
|
|
fragColor = ToVec4(resultFXAA, 1.0);
|
|
|
|
}
|