FXAA
Fragment shader : (http://blog.simonrodriguez.fr/articles/30-07-2016_implementing_fxaa.html)
uniform int byp; uniform sampler2D screenTexture; uniform float width; uniform float height; varying vec2 vUv; vec2 inverseScreenSize; const float FXAA_EDGE_THRESHOLD = 1.0/8.0; const float FXAA_EDGE_THRESHOLD_MIN = 1.0/16.0; const float FXAA_SUBPIX_TRIM = 1.0/4.0; const float FXAA_SUBPIX_CAP = 3.0/4.0; const int FXAA_SEARCH_STEPS = 12; const float FXAA_SEARCH_ACCELERATION = 2.0; float rgb2luma(vec3 rgb){ return rgb.y * (0.587/0.299) + rgb.x; } void main() { if(byp>0) { if(byp==8){ gl_FragColor = vec4(vec3(rgb2luma(texture2D(screenTexture, vUv).rgb)),1.0); return; } inverseScreenSize = vec2(1.0/width, 1.0/height); vec3 colorCenter = texture2D(screenTexture,vUv).rgb; float lumaM = rgb2luma(colorCenter); // Luma at the four direct neighbours of the current fragment. float lumaN = rgb2luma(texture2D(screenTexture,vUv+vec2(0.0,-1.0)*inverseScreenSize).rgb); float lumaS = rgb2luma(texture2D(screenTexture,vUv+vec2(0.0,1.0)*inverseScreenSize).rgb); float lumaW = rgb2luma(texture2D(screenTexture,vUv+vec2(-1.0,0.0)*inverseScreenSize).rgb); float lumaE = rgb2luma(texture2D(screenTexture,vUv+vec2(1.0,0.0)*inverseScreenSize).rgb); // Find the maximum and minimum luma around the current fragment. float rangeMin = min(lumaM,min(min(lumaN,lumaS),min(lumaW,lumaE))); float rangeMax = max(lumaM,max(max(lumaN,lumaS),max(lumaW,lumaE))); // Compute the delta. float range = rangeMax - rangeMin; // 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. if(range < max(FXAA_EDGE_THRESHOLD_MIN,rangeMax*FXAA_EDGE_THRESHOLD)){ gl_FragColor = texture2D (screenTexture, vUv); return; } else if (byp == 1) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); return; } float lumaNW = rgb2luma(texture2D(screenTexture,vUv+vec2(-1.0,-1.0)*inverseScreenSize).rgb); float lumaSE = rgb2luma(texture2D(screenTexture,vUv+vec2(1.0,1.0)*inverseScreenSize).rgb); float lumaSW = rgb2luma(texture2D(screenTexture,vUv+vec2(-1.0,1.0)*inverseScreenSize).rgb); float lumaNE = rgb2luma(texture2D(screenTexture,vUv+vec2(1.0,-1.0)*inverseScreenSize).rgb); float lumaVert = lumaN + lumaS; float lumaHori = lumaW + lumaE; // Same for corners float lumaWCorners = lumaNW + lumaSW; float lumaNCorners = lumaNW + lumaNE; float lumaECorners = lumaNE + lumaSE; float lumaSCorners = lumaSE + lumaSW; // Compute an estimation of the gradient along the horizontal and vertical axis. float edgeVert = abs((0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE)) + abs((0.50 * lumaW ) + (-1.0 * lumaM) + (0.50 * lumaE )) + abs((0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE)); float edgeHorz = abs((0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW)) + abs((0.50 * lumaN ) + (-1.0 * lumaM) + (0.50 * lumaS )) + abs((0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE)); // Is the local edge horizontal or vertical ? bool isHorizontal = edgeHorz >= edgeVert; if (isHorizontal && byp == 2) { gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); return; } else if(!isHorizontal && byp == 2) { gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); return; } // Select the two neighboring texels lumas in the opposite direction to the local edge. float luma1 = isHorizontal ? lumaN : lumaW; float luma2 = isHorizontal ? lumaS : lumaE; // Compute gradients in this direction. float gradient1 = luma1 - lumaM; float gradient2 = luma2 - lumaM; // Which direction is the steepest ? bool is1Steepest = abs(gradient1) >= abs(gradient2); if (is1Steepest && byp == 3) { gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); return; } else if(!is1Steepest && byp == 3) { gl_FragColor = vec4(0.2, 1.0, 0.2, 1.0); return; } // Gradient in the corresponding direction, normalized. float gradientScaled = 0.25*max(abs(gradient1),abs(gradient2)); // Choose the step size (one pixel) according to the edge direction. float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x; // Average luma in the correct direction. float lumaLocalAverage = 0.0; if(is1Steepest){ // Switch the direction stepLength = - stepLength; lumaLocalAverage = 0.5*(luma1 + lumaM); } else { lumaLocalAverage = 0.5*(luma2 + lumaM); } // Shift UV in the correct direction by half a pixel. vec2 currentUv = vUv; if(isHorizontal){ currentUv.y += stepLength * 0.5; } else { currentUv.x += stepLength * 0.5; } // Compute offset (for each iteration step) in the right direction. vec2 offset = isHorizontal ? vec2(inverseScreenSize.x,0.0) : vec2(0.0,inverseScreenSize.y); // Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster. vec2 uv1 = currentUv - offset; vec2 uv2 = currentUv + offset; // Read the lumas at both current extremities of the exploration segment, and compute the delta wrt to the local average luma. float lumaEnd1 = rgb2luma(texture2D(screenTexture,uv1).rgb); float lumaEnd2 = rgb2luma(texture2D(screenTexture,uv2).rgb); lumaEnd1 -= lumaLocalAverage; lumaEnd2 -= lumaLocalAverage; // If the luma deltas at the current extremities are larger than the local gradient, we have reached the side of the edge. bool reached1 = abs(lumaEnd1) >= gradientScaled; bool reached2 = abs(lumaEnd2) >= gradientScaled; bool reachedBoth = reached1 && reached2; // If the side is not reached, we continue to explore in this direction. if(!reached1){ uv1 -= offset; } if(!reached2){ uv2 += offset; } // If both sides have not been reached, continue to explore. if(!reachedBoth){ for(int i = 2; i < FXAA_SEARCH_STEPS; i++){ // If needed, read luma in 1st direction, compute delta. if(!reached1){ lumaEnd1 = rgb2luma(texture2D(screenTexture, uv1).rgb); lumaEnd1 = lumaEnd1 - lumaLocalAverage; } // If needed, read luma in opposite direction, compute delta. if(!reached2){ lumaEnd2 = rgb2luma(texture2D(screenTexture, uv2).rgb); lumaEnd2 = lumaEnd2 - lumaLocalAverage; } // If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge. reached1 = abs(lumaEnd1) >= gradientScaled; reached2 = abs(lumaEnd2) >= gradientScaled; reachedBoth = reached1 && reached2; // If the side is not reached, we continue to explore in this direction, with a variable quality. if(!reached1){ uv1 -= offset * FXAA_SEARCH_ACCELERATION; } if(!reached2){ uv2 += offset * FXAA_SEARCH_ACCELERATION; } // If both sides have been reached, stop the exploration. if(reachedBoth){ break;} } } // Compute the distances to each extremity of the edge. float distance1 = isHorizontal ? (vUv.x - uv1.x) : (vUv.y - uv1.y); float distance2 = isHorizontal ? (uv2.x - vUv.x) : (uv2.y - vUv.y); // In which direction is the extremity of the edge closer ? bool isDirection1 = distance1 < distance2; if (isDirection1 && byp == 4) { gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); return; } else if(!isDirection1 && byp == 4) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); return; } if (isDirection1 && is1Steepest && byp == 5) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); return; } else if(isDirection1 && !is1Steepest && byp == 5) { gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); return; } else if(!isDirection1 && is1Steepest && byp == 5) { gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); return; } else if(!isDirection1 && !is1Steepest && byp == 5) { gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); return; } float distanceFinal = min(distance1, distance2); // Length of the edge. float edgeThickness = (distance1 + distance2); // UV offset: read in the direction of the closest side of the edge. float pixelOffset = - distanceFinal / edgeThickness + 0.5; // Is the luma at center smaller than the local average ? bool islumaMSmaller = lumaM < lumaLocalAverage; // If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation). // (in the direction of the closer side of the edge.) bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != islumaMSmaller; // If the luma variation is incorrect, do not offset. float finalOffset = correctVariation ? pixelOffset : 0.0; // Sub-pixel shifting // Full weighted average of the luma over the 3x3 neighborhood. float lumaAverage = (1.0/9.0) * (lumaHori + lumaVert + lumaWCorners + lumaECorners); // Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood. float subPixelOffsetFinal = clamp(abs(lumaAverage - lumaM)/range - FXAA_SUBPIX_TRIM,0.0,FXAA_SUBPIX_CAP); if (finalOffset>subPixelOffsetFinal && byp == 6) { gl_FragColor = vec4(1.0, 0.0, 0.2, 1.0); return; } else if(!(finalOffset>subPixelOffsetFinal) && byp == 6) { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); return; } // Pick the biggest of the two offsets. finalOffset = max(finalOffset,subPixelOffsetFinal); // Compute the final UV coordinates. vec2 finalUv = vUv; if(isHorizontal){ finalUv.y += finalOffset * stepLength; } else { finalUv.x += finalOffset * stepLength; } // Read the color at the new UV coordinates, and use it. vec3 finalColor = texture2D(screenTexture,finalUv).rgb; gl_FragColor = vec4(finalColor, 1.0); } else { gl_FragColor=texture2D (screenTexture, vUv); } }