Skip to main content

FXAA_FRAG

Constant FXAA_FRAG 

Source
pub const FXAA_FRAG: &str = "// fxaa.frag \u{2014} fast approximate anti-aliasing, run on the finished LDR frame.\r\n//\r\n// The picture is built from hundreds of thousands of small quads, and every\r\n// one of them has a hard edge. The composite\'s unsharp mask makes those edges\r\n// crisper still. FXAA finds edges by local luma contrast, walks along them to\r\n// find their ends, and blends across them by exactly the amount the edge\r\n// geometry calls for. It is the standard Lottes algorithm at the \"quality\"\r\n// preset: twelve search steps, sub-pixel blending at three quarters.\r\n//\r\n// It runs between the composite and the HUD, so the interface on top stays\r\n// exactly as sharp as it was drawn.\r\n#version 330 core\r\n\r\nin vec2 f_uv;\r\nuniform sampler2D u_image;\r\nout vec4 o_color;\r\n\r\nconst float EDGE_THRESHOLD_MIN = 0.0312;\r\nconst float EDGE_THRESHOLD_MAX = 0.125;\r\nconst int   ITERATIONS         = 12;\r\nconst float SUBPIXEL_QUALITY   = 0.75;\r\n\r\n// Search step growth: tight for the first few, then long strides.\r\nfloat quality(int i) {\r\n    if (i < 5)  return 1.0;\r\n    if (i == 5) return 1.5;\r\n    if (i < 10) return 2.0;\r\n    if (i == 10) return 4.0;\r\n    return 8.0;\r\n}\r\n\r\nfloat luma(vec3 rgb) {\r\n    return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));\r\n}\r\n\r\nvoid main() {\r\n    vec2 texel = 1.0 / vec2(textureSize(u_image, 0));\r\n    vec3 colorCenter = texture(u_image, f_uv).rgb;\r\n    float lumaCenter = luma(colorCenter);\r\n    float lumaDown  = luma(textureOffset(u_image, f_uv, ivec2( 0, -1)).rgb);\r\n    float lumaUp    = luma(textureOffset(u_image, f_uv, ivec2( 0,  1)).rgb);\r\n    float lumaLeft  = luma(textureOffset(u_image, f_uv, ivec2(-1,  0)).rgb);\r\n    float lumaRight = luma(textureOffset(u_image, f_uv, ivec2( 1,  0)).rgb);\r\n\r\n    float lumaMin = min(lumaCenter, min(min(lumaDown, lumaUp), min(lumaLeft, lumaRight)));\r\n    float lumaMax = max(lumaCenter, max(max(lumaDown, lumaUp), max(lumaLeft, lumaRight)));\r\n    float lumaRange = lumaMax - lumaMin;\r\n\r\n    // Flat: nothing to do.\r\n    if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) {\r\n        o_color = vec4(colorCenter, 1.0);\r\n        return;\r\n    }\r\n\r\n    float lumaDownLeft  = luma(textureOffset(u_image, f_uv, ivec2(-1, -1)).rgb);\r\n    float lumaUpRight   = luma(textureOffset(u_image, f_uv, ivec2( 1,  1)).rgb);\r\n    float lumaUpLeft    = luma(textureOffset(u_image, f_uv, ivec2(-1,  1)).rgb);\r\n    float lumaDownRight = luma(textureOffset(u_image, f_uv, ivec2( 1, -1)).rgb);\r\n\r\n    float lumaDownUp      = lumaDown + lumaUp;\r\n    float lumaLeftRight   = lumaLeft + lumaRight;\r\n    float lumaLeftCorners = lumaDownLeft + lumaUpLeft;\r\n    float lumaDownCorners = lumaDownLeft + lumaDownRight;\r\n    float lumaRightCorners = lumaDownRight + lumaUpRight;\r\n    float lumaUpCorners   = lumaUpRight + lumaUpLeft;\r\n\r\n    float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners)\r\n                         + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0\r\n                         + abs(-2.0 * lumaRight + lumaRightCorners);\r\n    float edgeVertical   = abs(-2.0 * lumaUp + lumaUpCorners)\r\n                         + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0\r\n                         + abs(-2.0 * lumaDown + lumaDownCorners);\r\n    bool isHorizontal = edgeHorizontal >= edgeVertical;\r\n\r\n    float luma1 = isHorizontal ? lumaDown : lumaLeft;\r\n    float luma2 = isHorizontal ? lumaUp : lumaRight;\r\n    float gradient1 = luma1 - lumaCenter;\r\n    float gradient2 = luma2 - lumaCenter;\r\n    bool is1Steepest = abs(gradient1) >= abs(gradient2);\r\n    float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));\r\n\r\n    float stepLength = isHorizontal ? texel.y : texel.x;\r\n    float lumaLocalAverage;\r\n    if (is1Steepest) {\r\n        stepLength = -stepLength;\r\n        lumaLocalAverage = 0.5 * (luma1 + lumaCenter);\r\n    } else {\r\n        lumaLocalAverage = 0.5 * (luma2 + lumaCenter);\r\n    }\r\n\r\n    vec2 currentUv = f_uv;\r\n    if (isHorizontal) currentUv.y += stepLength * 0.5;\r\n    else              currentUv.x += stepLength * 0.5;\r\n\r\n    vec2 offset = isHorizontal ? vec2(texel.x, 0.0) : vec2(0.0, texel.y);\r\n    vec2 uv1 = currentUv - offset;\r\n    vec2 uv2 = currentUv + offset;\r\n    float lumaEnd1 = luma(texture(u_image, uv1).rgb) - lumaLocalAverage;\r\n    float lumaEnd2 = luma(texture(u_image, uv2).rgb) - lumaLocalAverage;\r\n    bool reached1 = abs(lumaEnd1) >= gradientScaled;\r\n    bool reached2 = abs(lumaEnd2) >= gradientScaled;\r\n    bool reachedBoth = reached1 && reached2;\r\n    if (!reached1) uv1 -= offset;\r\n    if (!reached2) uv2 += offset;\r\n\r\n    if (!reachedBoth) {\r\n        for (int i = 2; i < ITERATIONS; i++) {\r\n            if (!reached1) lumaEnd1 = luma(texture(u_image, uv1).rgb) - lumaLocalAverage;\r\n            if (!reached2) lumaEnd2 = luma(texture(u_image, uv2).rgb) - lumaLocalAverage;\r\n            reached1 = abs(lumaEnd1) >= gradientScaled;\r\n            reached2 = abs(lumaEnd2) >= gradientScaled;\r\n            reachedBoth = reached1 && reached2;\r\n            if (!reached1) uv1 -= offset * quality(i);\r\n            if (!reached2) uv2 += offset * quality(i);\r\n            if (reachedBoth) break;\r\n        }\r\n    }\r\n\r\n    float distance1 = isHorizontal ? (f_uv.x - uv1.x) : (f_uv.y - uv1.y);\r\n    float distance2 = isHorizontal ? (uv2.x - f_uv.x) : (uv2.y - f_uv.y);\r\n    bool isDirection1 = distance1 < distance2;\r\n    float distanceFinal = min(distance1, distance2);\r\n    float edgeThickness = distance1 + distance2;\r\n    float pixelOffset = -distanceFinal / edgeThickness + 0.5;\r\n\r\n    bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;\r\n    bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller;\r\n    float finalOffset = correctVariation ? pixelOffset : 0.0;\r\n\r\n    // Sub-pixel: blend by how far the centre is from the local average.\r\n    float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);\r\n    float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);\r\n    float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;\r\n    float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY;\r\n    finalOffset = max(finalOffset, subPixelOffsetFinal);\r\n\r\n    vec2 finalUv = f_uv;\r\n    if (isHorizontal) finalUv.y += finalOffset * stepLength;\r\n    else              finalUv.x += finalOffset * stepLength;\r\n\r\n    o_color = vec4(texture(u_image, finalUv).rgb, 1.0);\r\n}\r\n";
Expand description

Anti-aliasing on the finished LDR frame, before the HUD.