123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- import {
- Vector2
- } from 'three';
- /**
- * @module FXAAShader
- * @three_import import { FXAAShader } from 'three/addons/shaders/FXAAShader.js';
- */
- /**
- * FXAA algorithm from NVIDIA, C# implementation by Jasper Flick, GLSL port by Dave Hoskins.
- *
- * References:
- * - {@link http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf}.
- * - {@link https://catlikecoding.com/unity/tutorials/advanced-rendering/fxaa/}.
- *
- * @constant
- * @type {ShaderMaterial~Shader}
- */
- 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 */`
- uniform sampler2D tDiffuse;
- uniform vec2 resolution;
- varying vec2 vUv;
- #define EDGE_STEP_COUNT 6
- #define EDGE_GUESS 8.0
- #define EDGE_STEPS 1.0, 1.5, 2.0, 2.0, 2.0, 4.0
- const float edgeSteps[EDGE_STEP_COUNT] = float[EDGE_STEP_COUNT]( EDGE_STEPS );
- float _ContrastThreshold = 0.0312;
- float _RelativeThreshold = 0.063;
- float _SubpixelBlending = 1.0;
- vec4 Sample( sampler2D tex2D, vec2 uv ) {
- return texture( tex2D, uv );
- }
- float SampleLuminance( sampler2D tex2D, vec2 uv ) {
- return dot( Sample( tex2D, uv ).rgb, vec3( 0.3, 0.59, 0.11 ) );
- }
- float SampleLuminance( sampler2D tex2D, vec2 texSize, vec2 uv, float uOffset, float vOffset ) {
- uv += texSize * vec2(uOffset, vOffset);
- return SampleLuminance(tex2D, uv);
- }
- struct LuminanceData {
- float m, n, e, s, w;
- float ne, nw, se, sw;
- float highest, lowest, contrast;
- };
- LuminanceData SampleLuminanceNeighborhood( sampler2D tex2D, vec2 texSize, vec2 uv ) {
- LuminanceData l;
- l.m = SampleLuminance( tex2D, uv );
- l.n = SampleLuminance( tex2D, texSize, uv, 0.0, 1.0 );
- l.e = SampleLuminance( tex2D, texSize, uv, 1.0, 0.0 );
- l.s = SampleLuminance( tex2D, texSize, uv, 0.0, -1.0 );
- l.w = SampleLuminance( tex2D, texSize, uv, -1.0, 0.0 );
- l.ne = SampleLuminance( tex2D, texSize, uv, 1.0, 1.0 );
- l.nw = SampleLuminance( tex2D, texSize, uv, -1.0, 1.0 );
- l.se = SampleLuminance( tex2D, texSize, uv, 1.0, -1.0 );
- l.sw = SampleLuminance( tex2D, texSize, uv, -1.0, -1.0 );
- l.highest = max( max( max( max( l.n, l.e ), l.s ), l.w ), l.m );
- l.lowest = min( min( min( min( l.n, l.e ), l.s ), l.w ), l.m );
- l.contrast = l.highest - l.lowest;
- return l;
- }
- bool ShouldSkipPixel( LuminanceData l ) {
- float threshold = max( _ContrastThreshold, _RelativeThreshold * l.highest );
- return l.contrast < threshold;
- }
- float DeterminePixelBlendFactor( LuminanceData l ) {
- float f = 2.0 * ( l.n + l.e + l.s + l.w );
- f += l.ne + l.nw + l.se + l.sw;
- f *= 1.0 / 12.0;
- f = abs( f - l.m );
- f = clamp( f / l.contrast, 0.0, 1.0 );
- float blendFactor = smoothstep( 0.0, 1.0, f );
- return blendFactor * blendFactor * _SubpixelBlending;
- }
- struct EdgeData {
- bool isHorizontal;
- float pixelStep;
- float oppositeLuminance, gradient;
- };
- EdgeData DetermineEdge( vec2 texSize, LuminanceData l ) {
- EdgeData e;
- float horizontal =
- abs( l.n + l.s - 2.0 * l.m ) * 2.0 +
- abs( l.ne + l.se - 2.0 * l.e ) +
- abs( l.nw + l.sw - 2.0 * l.w );
- float vertical =
- abs( l.e + l.w - 2.0 * l.m ) * 2.0 +
- abs( l.ne + l.nw - 2.0 * l.n ) +
- abs( l.se + l.sw - 2.0 * l.s );
- e.isHorizontal = horizontal >= vertical;
- float pLuminance = e.isHorizontal ? l.n : l.e;
- float nLuminance = e.isHorizontal ? l.s : l.w;
- float pGradient = abs( pLuminance - l.m );
- float nGradient = abs( nLuminance - l.m );
- e.pixelStep = e.isHorizontal ? texSize.y : texSize.x;
- if (pGradient < nGradient) {
- e.pixelStep = -e.pixelStep;
- e.oppositeLuminance = nLuminance;
- e.gradient = nGradient;
- } else {
- e.oppositeLuminance = pLuminance;
- e.gradient = pGradient;
- }
- return e;
- }
- float DetermineEdgeBlendFactor( sampler2D tex2D, vec2 texSize, LuminanceData l, EdgeData e, vec2 uv ) {
- vec2 uvEdge = uv;
- vec2 edgeStep;
- if (e.isHorizontal) {
- uvEdge.y += e.pixelStep * 0.5;
- edgeStep = vec2( texSize.x, 0.0 );
- } else {
- uvEdge.x += e.pixelStep * 0.5;
- edgeStep = vec2( 0.0, texSize.y );
- }
- float edgeLuminance = ( l.m + e.oppositeLuminance ) * 0.5;
- float gradientThreshold = e.gradient * 0.25;
- vec2 puv = uvEdge + edgeStep * edgeSteps[0];
- float pLuminanceDelta = SampleLuminance( tex2D, puv ) - edgeLuminance;
- bool pAtEnd = abs( pLuminanceDelta ) >= gradientThreshold;
- for ( int i = 1; i < EDGE_STEP_COUNT && !pAtEnd; i++ ) {
- puv += edgeStep * edgeSteps[i];
- pLuminanceDelta = SampleLuminance( tex2D, puv ) - edgeLuminance;
- pAtEnd = abs( pLuminanceDelta ) >= gradientThreshold;
- }
- if ( !pAtEnd ) {
- puv += edgeStep * EDGE_GUESS;
- }
- vec2 nuv = uvEdge - edgeStep * edgeSteps[0];
- float nLuminanceDelta = SampleLuminance( tex2D, nuv ) - edgeLuminance;
- bool nAtEnd = abs( nLuminanceDelta ) >= gradientThreshold;
- for ( int i = 1; i < EDGE_STEP_COUNT && !nAtEnd; i++ ) {
- nuv -= edgeStep * edgeSteps[i];
- nLuminanceDelta = SampleLuminance( tex2D, nuv ) - edgeLuminance;
- nAtEnd = abs( nLuminanceDelta ) >= gradientThreshold;
- }
- if ( !nAtEnd ) {
- nuv -= edgeStep * EDGE_GUESS;
- }
- float pDistance, nDistance;
- if ( e.isHorizontal ) {
- pDistance = puv.x - uv.x;
- nDistance = uv.x - nuv.x;
- } else {
- pDistance = puv.y - uv.y;
- nDistance = uv.y - nuv.y;
- }
- float shortestDistance;
- bool deltaSign;
- if ( pDistance <= nDistance ) {
- shortestDistance = pDistance;
- deltaSign = pLuminanceDelta >= 0.0;
- } else {
- shortestDistance = nDistance;
- deltaSign = nLuminanceDelta >= 0.0;
- }
- if ( deltaSign == ( l.m - edgeLuminance >= 0.0 ) ) {
- return 0.0;
- }
- return 0.5 - shortestDistance / ( pDistance + nDistance );
- }
- vec4 ApplyFXAA( sampler2D tex2D, vec2 texSize, vec2 uv ) {
- LuminanceData luminance = SampleLuminanceNeighborhood( tex2D, texSize, uv );
- if ( ShouldSkipPixel( luminance ) ) {
- return Sample( tex2D, uv );
- }
- float pixelBlend = DeterminePixelBlendFactor( luminance );
- EdgeData edge = DetermineEdge( texSize, luminance );
- float edgeBlend = DetermineEdgeBlendFactor( tex2D, texSize, luminance, edge, uv );
- float finalBlend = max( pixelBlend, edgeBlend );
- if (edge.isHorizontal) {
- uv.y += edge.pixelStep * finalBlend;
- } else {
- uv.x += edge.pixelStep * finalBlend;
- }
- return Sample( tex2D, uv );
- }
- void main() {
- gl_FragColor = ApplyFXAA( tDiffuse, resolution.xy, vUv );
- }`
- };
- export { FXAAShader };
|