fxaa_optimized.frag 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #version 330 core
  2. out vec4 a_color;
  3. noperspective in vec2 v_texCoords;
  4. uniform sampler2D u_texture;
  5. float luminance(in vec3 color)
  6. {
  7. return dot(color, vec3(0.299, 0.587, 0.114));
  8. }
  9. float lumaSqr(in vec3 color)
  10. {
  11. return sqrt(luminance(color));
  12. }
  13. vec3 getTexture(in vec2 offset)
  14. {
  15. return texture2D(u_texture, v_texCoords + offset).rgb;
  16. }
  17. float quality(int i)
  18. {
  19. const int SIZE = 8;
  20. const int FIRST_SAMPLES_COUNT = 5;
  21. const float r[SIZE] = float[SIZE](1.5, 2.0, 2.0, 2.0, 2.0, 4.0, 6.0, 7.0);
  22. if(i < FIRST_SAMPLES_COUNT)
  23. {
  24. return 1;
  25. }else if(i >= FIRST_SAMPLES_COUNT + SIZE)
  26. {
  27. return 8;
  28. }else return r[i-FIRST_SAMPLES_COUNT];
  29. }
  30. /*
  31. default values example
  32. float edgeMinTreshold = 0.028;
  33. float edgeDarkTreshold = 0.125;
  34. int ITERATIONS = 12;
  35. float quaityMultiplier = 0.8;
  36. float SUBPIXEL_QUALITY = 0.95;
  37. */
  38. layout(std140) uniform u_FXAAData
  39. {
  40. float edgeMinTreshold;
  41. float edgeDarkTreshold;
  42. int ITERATIONS;
  43. float quaityMultiplier;
  44. float SUBPIXEL_QUALITY;
  45. }fxaaData;
  46. //http://blog.simonrodriguez.fr/articles/2016/07/implementing_fxaa.html
  47. void main()
  48. {
  49. vec3 colorCenter = getTexture(vec2(0,0)).rgb;
  50. // Luma at the current fragment
  51. float lumaCenter = lumaSqr(colorCenter);
  52. // Luma at the four direct neighbours of the current fragment.
  53. float lumaDown = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(0,-1)).rgb);
  54. float lumaUp = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(0,1)).rgb);
  55. float lumaLeft = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(-1,0)).rgb);
  56. float lumaRight = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(1,0)).rgb);
  57. // Find the maximum and minimum luma around the current fragment.
  58. float lumaMin = min(lumaCenter,min(min(lumaDown,lumaUp),min(lumaLeft,lumaRight)));
  59. float lumaMax = max(lumaCenter,max(max(lumaDown,lumaUp),max(lumaLeft,lumaRight)));
  60. // Compute the delta.
  61. float lumaRange = lumaMax - lumaMin;
  62. // If the luma variation is lower that a threshold (or if we are in a really dark area), we are not on an edge, don't perform any AA.
  63. if(lumaRange < max(fxaaData.edgeMinTreshold,lumaMax*fxaaData.edgeDarkTreshold))
  64. {
  65. a_color = vec4(colorCenter, 1);
  66. return;
  67. }
  68. // Query the 4 remaining corners lumas.
  69. float lumaDownLeft = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(-1,-1)).rgb);
  70. float lumaUpRight = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(1,1)).rgb);
  71. float lumaUpLeft = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(-1,1)).rgb);
  72. float lumaDownRight = lumaSqr(textureOffset(u_texture,v_texCoords,ivec2(1,-1)).rgb);
  73. // Combine the four edges lumas (using intermediary variables for future computations with the same values).
  74. float lumaDownUp = lumaDown + lumaUp;
  75. float lumaLeftRight = lumaLeft + lumaRight;
  76. // Same for corners
  77. float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
  78. float lumaDownCorners = lumaDownLeft + lumaDownRight;
  79. float lumaRightCorners = lumaDownRight + lumaUpRight;
  80. float lumaUpCorners = lumaUpRight + lumaUpLeft;
  81. // Compute an estimation of the gradient along the horizontal and vertical axis.
  82. float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp ) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
  83. float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
  84. // Is the local edge horizontal or vertical ?
  85. bool isHorizontal = (edgeHorizontal >= edgeVertical);
  86. // Select the two neighboring texels lumas in the opposite direction to the local edge.
  87. float luma1 = isHorizontal ? lumaDown : lumaLeft;
  88. float luma2 = isHorizontal ? lumaUp : lumaRight;
  89. // Compute gradients in this direction.
  90. float gradient1 = luma1 - lumaCenter;
  91. float gradient2 = luma2 - lumaCenter;
  92. // Which direction is the steepest ?
  93. bool is1Steepest = abs(gradient1) >= abs(gradient2);
  94. // Gradient in the corresponding direction, normalized.
  95. float gradientScaled = 0.25*max(abs(gradient1),abs(gradient2));
  96. vec2 inverseScreenSize = 1.f/textureSize(u_texture, 0);
  97. // Choose the step size (one pixel) according to the edge direction.
  98. float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x;
  99. // Average luma in the correct direction.
  100. float lumaLocalAverage = 0.0;
  101. if(is1Steepest)
  102. {
  103. // Switch the direction
  104. stepLength = - stepLength;
  105. lumaLocalAverage = 0.5*(luma1 + lumaCenter);
  106. }
  107. else
  108. {
  109. lumaLocalAverage = 0.5*(luma2 + lumaCenter);
  110. }
  111. // Shift UV in the correct direction by half a pixel.
  112. vec2 currentUv = v_texCoords;
  113. if(isHorizontal)
  114. {
  115. currentUv.y += stepLength * 0.5;
  116. } else
  117. {
  118. currentUv.x += stepLength * 0.5;
  119. }
  120. // Compute offset (for each iteration step) in the right direction.
  121. vec2 offset = isHorizontal ? vec2(inverseScreenSize.x,0.0) : vec2(0.0,inverseScreenSize.y);
  122. // Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster.
  123. vec2 uv1 = currentUv - offset;
  124. vec2 uv2 = currentUv + offset;
  125. // Read the lumas at both current extremities of the exploration segment, and compute the delta wrt to the local average luma.
  126. float lumaEnd1 = lumaSqr(texture(u_texture,uv1).rgb);
  127. float lumaEnd2 = lumaSqr(texture(u_texture,uv2).rgb);
  128. lumaEnd1 -= lumaLocalAverage;
  129. lumaEnd2 -= lumaLocalAverage;
  130. // If the luma deltas at the current extremities are larger than the local gradient, we have reached the side of the edge.
  131. bool reached1 = abs(lumaEnd1) >= gradientScaled;
  132. bool reached2 = abs(lumaEnd2) >= gradientScaled;
  133. bool reachedBoth = reached1 && reached2;
  134. // If the side is not reached, we continue to explore in this direction.
  135. if(!reached1){
  136. uv1 -= offset;
  137. }
  138. if(!reached2){
  139. uv2 += offset;
  140. }
  141. // If both sides have not been reached, continue to explore.
  142. if(!reachedBoth)
  143. {
  144. for(int i = 0; i < fxaaData.ITERATIONS; i++)
  145. {
  146. // If needed, read luma in 1st direction, compute delta.
  147. if(!reached1){
  148. lumaEnd1 = lumaSqr(texture(u_texture, uv1).rgb);
  149. lumaEnd1 = lumaEnd1 - lumaLocalAverage;
  150. }
  151. // If needed, read luma in opposite direction, compute delta.
  152. if(!reached2){
  153. lumaEnd2 = lumaSqr(texture(u_texture, uv2).rgb);
  154. lumaEnd2 = lumaEnd2 - lumaLocalAverage;
  155. }
  156. // If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge.
  157. reached1 = abs(lumaEnd1) >= gradientScaled;
  158. reached2 = abs(lumaEnd2) >= gradientScaled;
  159. reachedBoth = reached1 && reached2;
  160. // If the side is not reached, we continue to explore in this direction, with a variable quality.
  161. if(!reached1)
  162. {
  163. uv1 -= offset * quality(i) * fxaaData.quaityMultiplier;
  164. }
  165. if(!reached2)
  166. {
  167. uv2 += offset * quality(i) * fxaaData.quaityMultiplier;
  168. }
  169. // If both sides have been reached, stop the exploration.
  170. if(reachedBoth){ break;}
  171. }
  172. }
  173. // Compute the distances to each extremity of the edge.
  174. float distance1 = isHorizontal ? (v_texCoords.x - uv1.x) : (v_texCoords.y - uv1.y);
  175. float distance2 = isHorizontal ? (uv2.x - v_texCoords.x) : (uv2.y - v_texCoords.y);
  176. // In which direction is the extremity of the edge closer ?
  177. bool isDirection1 = distance1 < distance2;
  178. float distanceFinal = min(distance1, distance2);
  179. // Length of the edge.
  180. float edgeThickness = (distance1 + distance2);
  181. // UV offset: read in the direction of the closest side of the edge.
  182. float pixelOffset = - distanceFinal / edgeThickness + 0.5;
  183. // Is the luma at center smaller than the local average ?
  184. bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
  185. // If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation).
  186. // (in the direction of the closer side of the edge.)
  187. bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller;
  188. // If the luma variation is incorrect, do not offset.
  189. float finalOffset = correctVariation ? pixelOffset : 0.0;
  190. // Sub-pixel shifting
  191. // Full weighted average of the luma over the 3x3 neighborhood.
  192. float lumaAverage = (1.0/12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
  193. // Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood.
  194. float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter)/lumaRange,0.0,1.0);
  195. float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
  196. // Compute a sub-pixel offset based on this delta.
  197. float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * fxaaData.SUBPIXEL_QUALITY;
  198. // Pick the biggest of the two offsets.
  199. finalOffset = max(finalOffset,subPixelOffsetFinal);
  200. // Compute the final UV coordinates.
  201. vec2 finalUv = v_texCoords;
  202. if(isHorizontal){
  203. finalUv.y += finalOffset * stepLength;
  204. } else {
  205. finalUv.x += finalOffset * stepLength;
  206. }
  207. // Read the color at the new UV coordinates, and use it.
  208. vec3 finalColor = texture(u_texture, finalUv).rgb;
  209. a_color = vec4(finalColor, 1);
  210. }