import { Vector2 } from 'three'; /** * NVIDIA FXAA by Timothy Lottes * https://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf * - WebGL port by @supereggbert * http://www.glge.org/demos/fxaa/ * Further improved by Daniel Sturk */ const FXAAShader = { name: 'FXAAShader', uniforms: { 'tDiffuse': { value: null }, 'resolution': { value: new Vector2( 1 / 1024, 1 / 512 ) } }, vertexShader: /* glsl */` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: /* glsl */` precision highp float; uniform sampler2D tDiffuse; uniform vec2 resolution; varying vec2 vUv; // FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com) //---------------------------------------------------------------------------------- // File: es3-kepler\FXAA\assets\shaders/FXAA_DefaultES.frag // SDK Version: v3.00 // Email: gameworks@nvidia.com // Site: http://developer.nvidia.com/ // // Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //---------------------------------------------------------------------------------- #ifndef FXAA_DISCARD // // Only valid for PC OpenGL currently. // Probably will not work when FXAA_GREEN_AS_LUMA = 1. // // 1 = Use discard on pixels which don't need AA. // For APIs which enable concurrent TEX+ROP from same surface. // 0 = Return unchanged color on pixels which don't need AA. // #define FXAA_DISCARD 0 #endif /*--------------------------------------------------------------------------*/ #define FxaaTexTop(t, p) texture2D(t, p, -100.0) #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), -100.0) /*--------------------------------------------------------------------------*/ #define NUM_SAMPLES 5 // assumes colors have premultipliedAlpha, so that the calculated color contrast is scaled by alpha float contrast( vec4 a, vec4 b ) { vec4 diff = abs( a - b ); return max( max( max( diff.r, diff.g ), diff.b ), diff.a ); } /*============================================================================ FXAA3 QUALITY - PC ============================================================================*/ /*--------------------------------------------------------------------------*/ vec4 FxaaPixelShader( vec2 posM, sampler2D tex, vec2 fxaaQualityRcpFrame, float fxaaQualityEdgeThreshold, float fxaaQualityinvEdgeThreshold ) { vec4 rgbaM = FxaaTexTop(tex, posM); vec4 rgbaS = FxaaTexOff(tex, posM, vec2( 0.0, 1.0), fxaaQualityRcpFrame.xy); vec4 rgbaE = FxaaTexOff(tex, posM, vec2( 1.0, 0.0), fxaaQualityRcpFrame.xy); vec4 rgbaN = FxaaTexOff(tex, posM, vec2( 0.0,-1.0), fxaaQualityRcpFrame.xy); vec4 rgbaW = FxaaTexOff(tex, posM, vec2(-1.0, 0.0), fxaaQualityRcpFrame.xy); // . S . // W M E // . N . bool earlyExit = max( max( max( contrast( rgbaM, rgbaN ), contrast( rgbaM, rgbaS ) ), contrast( rgbaM, rgbaE ) ), contrast( rgbaM, rgbaW ) ) < fxaaQualityEdgeThreshold; // . 0 . // 0 0 0 // . 0 . #if (FXAA_DISCARD == 1) if(earlyExit) FxaaDiscard; #else if(earlyExit) return rgbaM; #endif float contrastN = contrast( rgbaM, rgbaN ); float contrastS = contrast( rgbaM, rgbaS ); float contrastE = contrast( rgbaM, rgbaE ); float contrastW = contrast( rgbaM, rgbaW ); float relativeVContrast = ( contrastN + contrastS ) - ( contrastE + contrastW ); relativeVContrast *= fxaaQualityinvEdgeThreshold; bool horzSpan = relativeVContrast > 0.; // . 1 . // 0 0 0 // . 1 . // 45 deg edge detection and corners of objects, aka V/H contrast is too similar if( abs( relativeVContrast ) < .3 ) { // locate the edge vec2 dirToEdge; dirToEdge.x = contrastE > contrastW ? 1. : -1.; dirToEdge.y = contrastS > contrastN ? 1. : -1.; // . 2 . . 1 . // 1 0 2 ~= 0 0 1 // . 1 . . 0 . // tap 2 pixels and see which ones are "outside" the edge, to // determine if the edge is vertical or horizontal vec4 rgbaAlongH = FxaaTexOff(tex, posM, vec2( dirToEdge.x, -dirToEdge.y ), fxaaQualityRcpFrame.xy); float matchAlongH = contrast( rgbaM, rgbaAlongH ); // . 1 . // 0 0 1 // . 0 H vec4 rgbaAlongV = FxaaTexOff(tex, posM, vec2( -dirToEdge.x, dirToEdge.y ), fxaaQualityRcpFrame.xy); float matchAlongV = contrast( rgbaM, rgbaAlongV ); // V 1 . // 0 0 1 // . 0 . relativeVContrast = matchAlongV - matchAlongH; relativeVContrast *= fxaaQualityinvEdgeThreshold; if( abs( relativeVContrast ) < .3 ) { // 45 deg edge // 1 1 . // 0 0 1 // . 0 1 // do a simple blur return mix( rgbaM, (rgbaN + rgbaS + rgbaE + rgbaW) * .25, .4 ); } horzSpan = relativeVContrast > 0.; } if(!horzSpan) rgbaN = rgbaW; if(!horzSpan) rgbaS = rgbaE; // . 0 . 1 // 1 0 1 -> 0 // . 0 . 1 bool pairN = contrast( rgbaM, rgbaN ) > contrast( rgbaM, rgbaS ); if(!pairN) rgbaN = rgbaS; vec2 offNP; offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; bool doneN = false; bool doneP = false; float nDist = 0.; float pDist = 0.; vec2 posN = posM; vec2 posP = posM; int iterationsUsed = 0; int iterationsUsedN = 0; int iterationsUsedP = 0; for( int i = 0; i < NUM_SAMPLES; i++ ) { iterationsUsed = i; float increment = float(i + 1); if(!doneN) { nDist += increment; posN = posM + offNP * nDist; vec4 rgbaEndN = FxaaTexTop(tex, posN.xy); doneN = contrast( rgbaEndN, rgbaM ) > contrast( rgbaEndN, rgbaN ); iterationsUsedN = i; } if(!doneP) { pDist += increment; posP = posM - offNP * pDist; vec4 rgbaEndP = FxaaTexTop(tex, posP.xy); doneP = contrast( rgbaEndP, rgbaM ) > contrast( rgbaEndP, rgbaN ); iterationsUsedP = i; } if(doneN || doneP) break; } if ( !doneP && !doneN ) return rgbaM; // failed to find end of edge float dist = min( doneN ? float( iterationsUsedN ) / float( NUM_SAMPLES - 1 ) : 1., doneP ? float( iterationsUsedP ) / float( NUM_SAMPLES - 1 ) : 1. ); // hacky way of reduces blurriness of mostly diagonal edges // but reduces AA quality dist = pow(dist, .5); dist = 1. - dist; return mix( rgbaM, rgbaN, dist * .5 ); } void main() { const float edgeDetectionQuality = .2; const float invEdgeDetectionQuality = 1. / edgeDetectionQuality; gl_FragColor = FxaaPixelShader( vUv, tDiffuse, resolution, edgeDetectionQuality, // [0,1] contrast needed, otherwise early discard invEdgeDetectionQuality ); } ` }; export { FXAAShader };