FXAAShader.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import {
  2. Vector2
  3. } from 'three';
  4. /**
  5. * @module FXAAShader
  6. * @three_import import { FXAAShader } from 'three/addons/shaders/FXAAShader.js';
  7. */
  8. /**
  9. * FXAA algorithm from NVIDIA, C# implementation by Jasper Flick, GLSL port by Dave Hoskins.
  10. *
  11. * References:
  12. * - {@link http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf}.
  13. * - {@link https://catlikecoding.com/unity/tutorials/advanced-rendering/fxaa/}.
  14. *
  15. * @constant
  16. * @type {ShaderMaterial~Shader}
  17. */
  18. const FXAAShader = {
  19. name: 'FXAAShader',
  20. uniforms: {
  21. 'tDiffuse': { value: null },
  22. 'resolution': { value: new Vector2( 1 / 1024, 1 / 512 ) }
  23. },
  24. vertexShader: /* glsl */`
  25. varying vec2 vUv;
  26. void main() {
  27. vUv = uv;
  28. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  29. }`,
  30. fragmentShader: /* glsl */`
  31. uniform sampler2D tDiffuse;
  32. uniform vec2 resolution;
  33. varying vec2 vUv;
  34. #define EDGE_STEP_COUNT 6
  35. #define EDGE_GUESS 8.0
  36. #define EDGE_STEPS 1.0, 1.5, 2.0, 2.0, 2.0, 4.0
  37. const float edgeSteps[EDGE_STEP_COUNT] = float[EDGE_STEP_COUNT]( EDGE_STEPS );
  38. float _ContrastThreshold = 0.0312;
  39. float _RelativeThreshold = 0.063;
  40. float _SubpixelBlending = 1.0;
  41. vec4 Sample( sampler2D tex2D, vec2 uv ) {
  42. return texture( tex2D, uv );
  43. }
  44. float SampleLuminance( sampler2D tex2D, vec2 uv ) {
  45. return dot( Sample( tex2D, uv ).rgb, vec3( 0.3, 0.59, 0.11 ) );
  46. }
  47. float SampleLuminance( sampler2D tex2D, vec2 texSize, vec2 uv, float uOffset, float vOffset ) {
  48. uv += texSize * vec2(uOffset, vOffset);
  49. return SampleLuminance(tex2D, uv);
  50. }
  51. struct LuminanceData {
  52. float m, n, e, s, w;
  53. float ne, nw, se, sw;
  54. float highest, lowest, contrast;
  55. };
  56. LuminanceData SampleLuminanceNeighborhood( sampler2D tex2D, vec2 texSize, vec2 uv ) {
  57. LuminanceData l;
  58. l.m = SampleLuminance( tex2D, uv );
  59. l.n = SampleLuminance( tex2D, texSize, uv, 0.0, 1.0 );
  60. l.e = SampleLuminance( tex2D, texSize, uv, 1.0, 0.0 );
  61. l.s = SampleLuminance( tex2D, texSize, uv, 0.0, -1.0 );
  62. l.w = SampleLuminance( tex2D, texSize, uv, -1.0, 0.0 );
  63. l.ne = SampleLuminance( tex2D, texSize, uv, 1.0, 1.0 );
  64. l.nw = SampleLuminance( tex2D, texSize, uv, -1.0, 1.0 );
  65. l.se = SampleLuminance( tex2D, texSize, uv, 1.0, -1.0 );
  66. l.sw = SampleLuminance( tex2D, texSize, uv, -1.0, -1.0 );
  67. l.highest = max( max( max( max( l.n, l.e ), l.s ), l.w ), l.m );
  68. l.lowest = min( min( min( min( l.n, l.e ), l.s ), l.w ), l.m );
  69. l.contrast = l.highest - l.lowest;
  70. return l;
  71. }
  72. bool ShouldSkipPixel( LuminanceData l ) {
  73. float threshold = max( _ContrastThreshold, _RelativeThreshold * l.highest );
  74. return l.contrast < threshold;
  75. }
  76. float DeterminePixelBlendFactor( LuminanceData l ) {
  77. float f = 2.0 * ( l.n + l.e + l.s + l.w );
  78. f += l.ne + l.nw + l.se + l.sw;
  79. f *= 1.0 / 12.0;
  80. f = abs( f - l.m );
  81. f = clamp( f / l.contrast, 0.0, 1.0 );
  82. float blendFactor = smoothstep( 0.0, 1.0, f );
  83. return blendFactor * blendFactor * _SubpixelBlending;
  84. }
  85. struct EdgeData {
  86. bool isHorizontal;
  87. float pixelStep;
  88. float oppositeLuminance, gradient;
  89. };
  90. EdgeData DetermineEdge( vec2 texSize, LuminanceData l ) {
  91. EdgeData e;
  92. float horizontal =
  93. abs( l.n + l.s - 2.0 * l.m ) * 2.0 +
  94. abs( l.ne + l.se - 2.0 * l.e ) +
  95. abs( l.nw + l.sw - 2.0 * l.w );
  96. float vertical =
  97. abs( l.e + l.w - 2.0 * l.m ) * 2.0 +
  98. abs( l.ne + l.nw - 2.0 * l.n ) +
  99. abs( l.se + l.sw - 2.0 * l.s );
  100. e.isHorizontal = horizontal >= vertical;
  101. float pLuminance = e.isHorizontal ? l.n : l.e;
  102. float nLuminance = e.isHorizontal ? l.s : l.w;
  103. float pGradient = abs( pLuminance - l.m );
  104. float nGradient = abs( nLuminance - l.m );
  105. e.pixelStep = e.isHorizontal ? texSize.y : texSize.x;
  106. if (pGradient < nGradient) {
  107. e.pixelStep = -e.pixelStep;
  108. e.oppositeLuminance = nLuminance;
  109. e.gradient = nGradient;
  110. } else {
  111. e.oppositeLuminance = pLuminance;
  112. e.gradient = pGradient;
  113. }
  114. return e;
  115. }
  116. float DetermineEdgeBlendFactor( sampler2D tex2D, vec2 texSize, LuminanceData l, EdgeData e, vec2 uv ) {
  117. vec2 uvEdge = uv;
  118. vec2 edgeStep;
  119. if (e.isHorizontal) {
  120. uvEdge.y += e.pixelStep * 0.5;
  121. edgeStep = vec2( texSize.x, 0.0 );
  122. } else {
  123. uvEdge.x += e.pixelStep * 0.5;
  124. edgeStep = vec2( 0.0, texSize.y );
  125. }
  126. float edgeLuminance = ( l.m + e.oppositeLuminance ) * 0.5;
  127. float gradientThreshold = e.gradient * 0.25;
  128. vec2 puv = uvEdge + edgeStep * edgeSteps[0];
  129. float pLuminanceDelta = SampleLuminance( tex2D, puv ) - edgeLuminance;
  130. bool pAtEnd = abs( pLuminanceDelta ) >= gradientThreshold;
  131. for ( int i = 1; i < EDGE_STEP_COUNT && !pAtEnd; i++ ) {
  132. puv += edgeStep * edgeSteps[i];
  133. pLuminanceDelta = SampleLuminance( tex2D, puv ) - edgeLuminance;
  134. pAtEnd = abs( pLuminanceDelta ) >= gradientThreshold;
  135. }
  136. if ( !pAtEnd ) {
  137. puv += edgeStep * EDGE_GUESS;
  138. }
  139. vec2 nuv = uvEdge - edgeStep * edgeSteps[0];
  140. float nLuminanceDelta = SampleLuminance( tex2D, nuv ) - edgeLuminance;
  141. bool nAtEnd = abs( nLuminanceDelta ) >= gradientThreshold;
  142. for ( int i = 1; i < EDGE_STEP_COUNT && !nAtEnd; i++ ) {
  143. nuv -= edgeStep * edgeSteps[i];
  144. nLuminanceDelta = SampleLuminance( tex2D, nuv ) - edgeLuminance;
  145. nAtEnd = abs( nLuminanceDelta ) >= gradientThreshold;
  146. }
  147. if ( !nAtEnd ) {
  148. nuv -= edgeStep * EDGE_GUESS;
  149. }
  150. float pDistance, nDistance;
  151. if ( e.isHorizontal ) {
  152. pDistance = puv.x - uv.x;
  153. nDistance = uv.x - nuv.x;
  154. } else {
  155. pDistance = puv.y - uv.y;
  156. nDistance = uv.y - nuv.y;
  157. }
  158. float shortestDistance;
  159. bool deltaSign;
  160. if ( pDistance <= nDistance ) {
  161. shortestDistance = pDistance;
  162. deltaSign = pLuminanceDelta >= 0.0;
  163. } else {
  164. shortestDistance = nDistance;
  165. deltaSign = nLuminanceDelta >= 0.0;
  166. }
  167. if ( deltaSign == ( l.m - edgeLuminance >= 0.0 ) ) {
  168. return 0.0;
  169. }
  170. return 0.5 - shortestDistance / ( pDistance + nDistance );
  171. }
  172. vec4 ApplyFXAA( sampler2D tex2D, vec2 texSize, vec2 uv ) {
  173. LuminanceData luminance = SampleLuminanceNeighborhood( tex2D, texSize, uv );
  174. if ( ShouldSkipPixel( luminance ) ) {
  175. return Sample( tex2D, uv );
  176. }
  177. float pixelBlend = DeterminePixelBlendFactor( luminance );
  178. EdgeData edge = DetermineEdge( texSize, luminance );
  179. float edgeBlend = DetermineEdgeBlendFactor( tex2D, texSize, luminance, edge, uv );
  180. float finalBlend = max( pixelBlend, edgeBlend );
  181. if (edge.isHorizontal) {
  182. uv.y += edge.pixelStep * finalBlend;
  183. } else {
  184. uv.x += edge.pixelStep * finalBlend;
  185. }
  186. return Sample( tex2D, uv );
  187. }
  188. void main() {
  189. gl_FragColor = ApplyFXAA( tDiffuse, resolution.xy, vUv );
  190. }`
  191. };
  192. export { FXAAShader };