299 lines
6.7 KiB
JavaScript
299 lines
6.7 KiB
JavaScript
|
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 };
|