fxaa.fx (10032B)
1 #include "ReShade.fxh" 2 3 4 /** 5 * @license 6 * Copyright (c) 2011 NVIDIA Corporation. All rights reserved. 7 * 8 * TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED 9 * *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS 10 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT,IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA 12 * OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT, OR 13 * CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS 14 * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY 15 * OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 16 * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17 */ 18 19 /* 20 FXAA_PRESET - Choose compile-in knob preset 0-5. 21 ------------------------------------------------------------------------------ 22 FXAA_EDGE_THRESHOLD - The minimum amount of local contrast required 23 to apply algorithm. 24 1.0/3.0 - too little 25 1.0/4.0 - good start 26 1.0/8.0 - applies to more edges 27 1.0/16.0 - overkill 28 ------------------------------------------------------------------------------ 29 FXAA_EDGE_THRESHOLD_MIN - Trims the algorithm from processing darks. 30 Perf optimization. 31 1.0/32.0 - visible limit (smaller isn't visible) 32 1.0/16.0 - good compromise 33 1.0/12.0 - upper limit (seeing artifacts) 34 ------------------------------------------------------------------------------ 35 FXAA_SEARCH_STEPS - Maximum number of search steps for end of span. 36 ------------------------------------------------------------------------------ 37 FXAA_SEARCH_THRESHOLD - Controls when to stop searching. 38 1.0/4.0 - seems to be the best quality wise 39 ------------------------------------------------------------------------------ 40 FXAA_SUBPIX_TRIM - Controls sub-pixel aliasing removal. 41 1.0/2.0 - low removal 42 1.0/3.0 - medium removal 43 1.0/4.0 - default removal 44 1.0/8.0 - high removal 45 0.0 - complete removal 46 ------------------------------------------------------------------------------ 47 FXAA_SUBPIX_CAP - Insures fine detail is not completely removed. 48 This is important for the transition of sub-pixel detail, 49 like fences and wires. 50 3.0/4.0 - default (medium amount of filtering) 51 7.0/8.0 - high amount of filtering 52 1.0 - no capping of sub-pixel aliasing removal 53 */ 54 55 56 uniform float2 BufferToViewportRatio < source = "buffer_to_viewport_ratio"; >; 57 uniform float2 ViewportSize < source = "viewportsize"; >; 58 59 sampler2D sBackBuffer{Texture=ReShade::BackBufferTex;AddressU=CLAMP;AddressV=CLAMP;AddressW=CLAMP;MagFilter=LINEAR;MinFilter=LINEAR;}; 60 61 62 #ifndef FXAA_PRESET 63 #define FXAA_PRESET 6 64 #endif 65 #if (FXAA_PRESET == 3) 66 #define FXAA_EDGE_THRESHOLD (1.0/8.0) 67 #define FXAA_EDGE_THRESHOLD_MIN (1.0/16.0) 68 #define FXAA_SEARCH_STEPS 16 69 #define FXAA_SEARCH_THRESHOLD (1.0/4.0) 70 #define FXAA_SUBPIX_CAP (3.0/4.0) 71 #define FXAA_SUBPIX_TRIM (1.0/4.0) 72 #endif 73 #if (FXAA_PRESET == 4) 74 #define FXAA_EDGE_THRESHOLD (1.0/8.0) 75 #define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) 76 #define FXAA_SEARCH_STEPS 24 77 #define FXAA_SEARCH_THRESHOLD (1.0/4.0) 78 #define FXAA_SUBPIX_CAP (3.0/4.0) 79 #define FXAA_SUBPIX_TRIM (1.0/4.0) 80 #endif 81 #if (FXAA_PRESET == 5) 82 #define FXAA_EDGE_THRESHOLD (1.0/8.0) 83 #define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) 84 #define FXAA_SEARCH_STEPS 32 85 #define FXAA_SEARCH_THRESHOLD (1.0/4.0) 86 #define FXAA_SUBPIX_CAP (3.0/4.0) 87 #define FXAA_SUBPIX_TRIM (1.0/4.0) 88 #endif 89 #if (FXAA_PRESET == 6) 90 #define FXAA_EDGE_THRESHOLD (1.0/8.0) 91 #define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) 92 #define FXAA_SEARCH_STEPS 32 93 #define FXAA_SEARCH_THRESHOLD (1.0/4.0) 94 #define FXAA_SUBPIX_CAP (1.0) 95 #define FXAA_SUBPIX_TRIM (0.0) 96 #endif 97 98 #define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM)) 99 100 // Return the luma, the estimation of luminance from rgb inputs. 101 // This approximates luma using one FMA instruction, 102 // skipping normalization and tossing out blue. 103 // FxaaLuma() will range 0.0 to 2.963210702. 104 float FxaaLuma(float3 rgb) { 105 return rgb.y * (0.587/0.299) + rgb.x; 106 } 107 108 float3 FxaaLerp3(float3 a, float3 b, float amountOfA) { 109 return (-float3(amountOfA, amountOfA, amountOfA) * b) + ((a * float3(amountOfA, amountOfA, amountOfA)) + b); 110 } 111 112 float4 FxaaTexOff(sampler2D tex, float2 pos, int2 off, float2 rcpFrame) { 113 float x = pos.x + float(off.x) * rcpFrame.x; 114 float y = pos.y + float(off.y) * rcpFrame.y; 115 return tex2D(tex, float2(x, y)); 116 } 117 118 // pos is the output of FxaaVertexShader interpolated across screen. 119 // xy -> actual texture position {0.0 to 1.0} 120 // rcpFrame should be a uniform equal to {1.0/frameWidth, 1.0/frameHeight} 121 float3 FxaaPixelShader(float2 pos, sampler2D tex, float2 rcpFrame) 122 { 123 float3 rgbN = FxaaTexOff(tex, pos.xy, int2( 0,-1), rcpFrame).xyz; 124 float3 rgbW = FxaaTexOff(tex, pos.xy, int2(-1, 0), rcpFrame).xyz; 125 float3 rgbM = FxaaTexOff(tex, pos.xy, int2( 0, 0), rcpFrame).xyz; 126 float3 rgbE = FxaaTexOff(tex, pos.xy, int2( 1, 0), rcpFrame).xyz; 127 float3 rgbS = FxaaTexOff(tex, pos.xy, int2( 0, 1), rcpFrame).xyz; 128 129 float lumaN = FxaaLuma(rgbN); 130 float lumaW = FxaaLuma(rgbW); 131 float lumaM = FxaaLuma(rgbM); 132 float lumaE = FxaaLuma(rgbE); 133 float lumaS = FxaaLuma(rgbS); 134 float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE))); 135 float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE))); 136 137 float range = rangeMax - rangeMin; 138 if(range < max(FXAA_EDGE_THRESHOLD_MIN, rangeMax * FXAA_EDGE_THRESHOLD)) 139 { 140 return rgbM; 141 } 142 143 float3 rgbL = rgbN + rgbW + rgbM + rgbE + rgbS; 144 145 float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25; 146 float rangeL = abs(lumaL - lumaM); 147 float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE; 148 blendL = min(FXAA_SUBPIX_CAP, blendL); 149 150 float3 rgbNW = FxaaTexOff(tex, pos.xy, int2(-1,-1), rcpFrame).xyz; 151 float3 rgbNE = FxaaTexOff(tex, pos.xy, int2( 1,-1), rcpFrame).xyz; 152 float3 rgbSW = FxaaTexOff(tex, pos.xy, int2(-1, 1), rcpFrame).xyz; 153 float3 rgbSE = FxaaTexOff(tex, pos.xy, int2( 1, 1), rcpFrame).xyz; 154 rgbL += (rgbNW + rgbNE + rgbSW + rgbSE); 155 rgbL *= (1.0/float3(9.0, 9.0, 9.0)); 156 157 float lumaNW = FxaaLuma(rgbNW); 158 float lumaNE = FxaaLuma(rgbNE); 159 float lumaSW = FxaaLuma(rgbSW); 160 float lumaSE = FxaaLuma(rgbSE); 161 162 float edgeVert = 163 abs((0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE)) + 164 abs((0.50 * lumaW ) + (-1.0 * lumaM) + (0.50 * lumaE )) + 165 abs((0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE)); 166 float edgeHorz = 167 abs((0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW)) + 168 abs((0.50 * lumaN ) + (-1.0 * lumaM) + (0.50 * lumaS )) + 169 abs((0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE)); 170 171 bool horzSpan = edgeHorz >= edgeVert; 172 float lengthSign = horzSpan ? -rcpFrame.y : -rcpFrame.x; 173 174 if(!horzSpan) 175 { 176 lumaN = lumaW; 177 lumaS = lumaE; 178 } 179 180 float gradientN = abs(lumaN - lumaM); 181 float gradientS = abs(lumaS - lumaM); 182 lumaN = (lumaN + lumaM) * 0.5; 183 lumaS = (lumaS + lumaM) * 0.5; 184 185 if (gradientN < gradientS) 186 { 187 lumaN = lumaS; 188 lumaN = lumaS; 189 gradientN = gradientS; 190 lengthSign *= -1.0; 191 } 192 193 float2 posN; 194 posN.x = pos.x + (horzSpan ? 0.0 : lengthSign * 0.5); 195 posN.y = pos.y + (horzSpan ? lengthSign * 0.5 : 0.0); 196 197 gradientN *= FXAA_SEARCH_THRESHOLD; 198 199 float2 posP = posN; 200 float2 offNP = horzSpan ? float2(rcpFrame.x, 0.0) : float2(0.0, rcpFrame.y); 201 float lumaEndN = lumaN; 202 float lumaEndP = lumaN; 203 bool doneN = false; 204 bool doneP = false; 205 posN += offNP * float2(-1.0, -1.0); 206 posP += offNP * float2( 1.0, 1.0); 207 208 for(int i = 0; i < FXAA_SEARCH_STEPS; i++) { 209 if(!doneN) 210 { 211 lumaEndN = FxaaLuma(tex2D(tex, posN.xy).xyz); 212 } 213 if(!doneP) 214 { 215 lumaEndP = FxaaLuma(tex2D(tex, posP.xy).xyz); 216 } 217 218 doneN = doneN || (abs(lumaEndN - lumaN) >= gradientN); 219 doneP = doneP || (abs(lumaEndP - lumaN) >= gradientN); 220 221 if(doneN && doneP) 222 { 223 break; 224 } 225 if(!doneN) 226 { 227 posN -= offNP; 228 } 229 if(!doneP) 230 { 231 posP += offNP; 232 } 233 } 234 235 float dstN = horzSpan ? pos.x - posN.x : pos.y - posN.y; 236 float dstP = horzSpan ? posP.x - pos.x : posP.y - pos.y; 237 bool directionN = dstN < dstP; 238 lumaEndN = directionN ? lumaEndN : lumaEndP; 239 240 if(((lumaM - lumaN) < 0.0) == ((lumaEndN - lumaN) < 0.0)) 241 { 242 lengthSign = 0.0; 243 } 244 245 246 float spanLength = (dstP + dstN); 247 dstN = directionN ? dstN : dstP; 248 float subPixelOffset = (0.5 + (dstN * (-1.0/spanLength))) * lengthSign; 249 float3 rgbF = tex2D(tex, float2( 250 pos.x + (horzSpan ? 0.0 : subPixelOffset), 251 pos.y + (horzSpan ? subPixelOffset : 0.0))).xyz; 252 return FxaaLerp3(rgbL, rgbF, blendL); 253 } 254 255 float4 PS_FXAA(float4 vpos: SV_Position, float2 vTexCoord : TEXCOORD0) : SV_Target 256 { 257 float3 color = FxaaPixelShader(vTexCoord, sBackBuffer, 1.0 / (ViewportSize*BufferToViewportRatio)); 258 259 return float4(color, 1.0); 260 } 261 262 263 264 technique FXAA 265 { 266 pass 267 { 268 VertexShader = PostProcessVS; 269 PixelShader = PS_FXAA; 270 } 271 }