Shader "Hidden/FXAA" { Properties { _MainTex ("Texture", 2D) = "white" {} } CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; half4 _MainTex_TexelSize; half _ContrastThreshold, _RelativeThreshold; half _SubpixelBlending; struct VertexData { half4 vertex : POSITION; half2 uv : TEXCOORD0; }; struct Interpolators { half4 pos : SV_POSITION; half2 uv : TEXCOORD0; }; Interpolators VertexProgram (VertexData v) { Interpolators i; i.pos = UnityObjectToClipPos(v.vertex); i.uv = v.uv; return i; } half4 Sample (half2 uv) { return tex2Dlod(_MainTex, half4(uv, 0, 0)); } half SampleLuminance (half2 uv) { return Sample(uv).g; // #if defined(LUMINANCE_GREEN) // return Sample(uv).g; // #else // return Sample(uv).a; // #endif } half SampleLuminance (half2 uv, half uOffset, half vOffset) { uv += _MainTex_TexelSize * half2(uOffset, vOffset); return SampleLuminance(uv); } struct LuminanceData { half m, n, e, s, w; half ne, nw, se, sw; half highest, lowest, contrast; }; LuminanceData SampleLuminanceNeighborhood (half2 uv) { LuminanceData l; l.m = SampleLuminance(uv); l.n = SampleLuminance(uv, 0, 1); l.e = SampleLuminance(uv, 1, 0); l.s = SampleLuminance(uv, 0, -1); l.w = SampleLuminance(uv, -1, 0); l.ne = SampleLuminance(uv, 1, 1); l.nw = SampleLuminance(uv, -1, 1); l.se = SampleLuminance(uv, 1, -1); l.sw = SampleLuminance(uv, -1, -1); 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) { half threshold = max(_ContrastThreshold, _RelativeThreshold * l.highest); return l.contrast < threshold; } half DeterminePixelBlendFactor (LuminanceData l) { half filter = 2 * (l.n + l.e + l.s + l.w); filter += l.ne + l.nw + l.se + l.sw; filter *= 1.0 / 12.0; filter = abs(filter - l.m); filter = saturate(filter / l.contrast); half blendFactor = smoothstep(0, 1, filter); return blendFactor * blendFactor * _SubpixelBlending; } struct EdgeData { bool isHorizontal; half pixelStep; half oppositeLuminance, gradient; }; EdgeData DetermineEdge (LuminanceData l) { EdgeData e; half horizontal = abs(l.n + l.s - 2 * l.m) * 2 + abs(l.ne + l.se - 2 * l.e) + abs(l.nw + l.sw - 2 * l.w); half vertical = abs(l.e + l.w - 2 * l.m) * 2 + abs(l.ne + l.nw - 2 * l.n) + abs(l.se + l.sw - 2 * l.s); e.isHorizontal = horizontal >= vertical; half pLuminance = e.isHorizontal ? l.n : l.e; half nLuminance = e.isHorizontal ? l.s : l.w; half pGradient = abs(pLuminance - l.m); half nGradient = abs(nLuminance - l.m); e.pixelStep = e.isHorizontal ? _MainTex_TexelSize.y : _MainTex_TexelSize.x; if (pGradient < nGradient) { e.pixelStep = -e.pixelStep; e.oppositeLuminance = nLuminance; e.gradient = nGradient; } else { e.oppositeLuminance = pLuminance; e.gradient = pGradient; } return e; } #if defined(LOW_QUALITY) #define EDGE_STEP_COUNT 4 #define EDGE_STEPS 1, 1.5, 2, 4 #define EDGE_GUESS 12 #else #define EDGE_STEP_COUNT 10 #define EDGE_STEPS 1, 1.5, 2, 2, 2, 2, 2, 2, 2, 4 #define EDGE_GUESS 8 #endif static const half edgeSteps[EDGE_STEP_COUNT] = { EDGE_STEPS }; half DetermineEdgeBlendFactor (LuminanceData l, EdgeData e, half2 uv) { half2 uvEdge = uv; half2 edgeStep; if (e.isHorizontal) { uvEdge.y += e.pixelStep * 0.5; edgeStep = half2(_MainTex_TexelSize.x, 0); } else { uvEdge.x += e.pixelStep * 0.5; edgeStep = half2(0, _MainTex_TexelSize.y); } half edgeLuminance = (l.m + e.oppositeLuminance) * 0.5; half gradientThreshold = e.gradient * 0.25; half2 puv = uvEdge + edgeStep * edgeSteps[0]; half pLuminanceDelta = SampleLuminance(puv) - edgeLuminance; bool pAtEnd = abs(pLuminanceDelta) >= gradientThreshold; UNITY_UNROLL for (int i = 1; i < EDGE_STEP_COUNT && !pAtEnd; i++) { puv += edgeStep * edgeSteps[i]; pLuminanceDelta = SampleLuminance(puv) - edgeLuminance; pAtEnd = abs(pLuminanceDelta) >= gradientThreshold; } if (!pAtEnd) { puv += edgeStep * EDGE_GUESS; } half2 nuv = uvEdge - edgeStep * edgeSteps[0]; half nLuminanceDelta = SampleLuminance(nuv) - edgeLuminance; bool nAtEnd = abs(nLuminanceDelta) >= gradientThreshold; UNITY_UNROLL for (int i = 1; i < EDGE_STEP_COUNT && !nAtEnd; i++) { nuv -= edgeStep * edgeSteps[i]; nLuminanceDelta = SampleLuminance(nuv) - edgeLuminance; nAtEnd = abs(nLuminanceDelta) >= gradientThreshold; } if (!nAtEnd) { nuv -= edgeStep * EDGE_GUESS; } half 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; } half shortestDistance; bool deltaSign; if (pDistance <= nDistance) { shortestDistance = pDistance; deltaSign = pLuminanceDelta >= 0; } else { shortestDistance = nDistance; deltaSign = nLuminanceDelta >= 0; } if (deltaSign == (l.m - edgeLuminance >= 0)) { return 0; } return 0.5 - shortestDistance / (pDistance + nDistance); } half4 ApplyFXAA (half2 uv) { LuminanceData l = SampleLuminanceNeighborhood(uv); if (ShouldSkipPixel(l)) { return Sample(uv); } half pixelBlend = DeterminePixelBlendFactor(l); EdgeData e = DetermineEdge(l); half edgeBlend = DetermineEdgeBlendFactor(l, e, uv); half finalBlend = max(pixelBlend, edgeBlend); if (e.isHorizontal) { uv.y += e.pixelStep * finalBlend; } else { uv.x += e.pixelStep * finalBlend; } return half4(Sample(uv).rgb, l.m); } ENDCG SubShader { Cull Off ZTest Always ZWrite Off Pass { // 0 luminancePass CGPROGRAM #pragma vertex VertexProgram #pragma fragment FragmentProgram // #pragma enable_d3d11_debug_symbols #pragma multi_compile _ GAMMA_BLENDING half4 FragmentProgram (Interpolators i) : SV_Target { half4 sample = tex2D(_MainTex, i.uv); sample.rgb = saturate(sample.rgb); sample.a = LinearRgbToLuminance(sample.rgb); #if defined(GAMMA_BLENDING) sample.rgb = LinearToGammaSpace(sample.rgb); #endif return sample; } ENDCG } Pass { // 1 fxaaPass CGPROGRAM #pragma vertex VertexProgram #pragma fragment FragmentProgram // #pragma enable_d3d11_debug_symbols // #pragma multi_compile _ LUMINANCE_GREEN #pragma multi_compile _ LOW_QUALITY #pragma multi_compile _ GAMMA_BLENDING half4 FragmentProgram (Interpolators i) : SV_Target { half4 sample = ApplyFXAA(i.uv); #if defined(GAMMA_BLENDING) sample.rgb = GammaToLinearSpace(sample.rgb); #endif return sample; } ENDCG } } }