├── ui.png ├── shader_order_example.png ├── LICENSE.md ├── README.md └── TFAA.fx /ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakobPCoder/ReshadeTFAA/HEAD/ui.png -------------------------------------------------------------------------------- /shader_order_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JakobPCoder/ReshadeTFAA/HEAD/shader_order_example.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | # Copyright Notice 3 | - Temporal Filter Anti Aliasing | TFAA 4 | - First published 2022 - Copyright, Jakob Wapenhensch 5 | - https://creativecommons.org/licenses/by-nc/4.0/ 6 | - https://creativecommons.org/licenses/by-nc/4.0/legalcode 7 | 8 | # Human-readable summary of the License 9 | - NOT a substitute for https://creativecommons.org/licenses/by-nc/4.0/legalcode 10 | 11 | ## You are free to: 12 | - Share — copy and redistribute the material in any medium or format 13 | - Adapt — remix, transform, and build upon the material 14 | - The licensor cannot revoke these freedoms as long as you follow the license terms. 15 | 16 | ## Under the following terms: 17 | - Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 18 | - NonCommercial — You may not use the material for commercial purposes. 19 | - No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 20 | 21 | ## Notices: 22 | - You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. 23 | - No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reshade TFAA 2 | - This is a work in progress Reshade shader, which acts as an addon to other, non-temporal anti-aliasing methods. 3 | - It adds a Temporal filter component similar to the ones found in SMAA (1Tx, Filmic SMAA T2x), TAAU, most otherwise unamed TAA implementations and even FSR2. 4 | - It requires Marty McFly's [LAUNCHPAD.fx](https://github.com/martymcmodding/iMMERSE/blob/main/Shaders/MartysMods_LAUNCHPAD.fx). 5 | 6 | 7 | # Copyright Notice 8 | - Temporal Filter Anti-Aliasing | TFAA 9 | - First published 2022 - Copyright, Jakob Wapenhensch 10 | - License File [HERE](LICENSE) 11 | - https://creativecommons.org/licenses/by-nc/4.0/ 12 | - https://creativecommons.org/licenses/by-nc/4.0/legalcode 13 | 14 | # Example Shader Usage 15 | ![](shader_order_example.png) 16 | 17 | ![](ui.png) 18 | 19 | 20 | 21 | # Updates 22 | - 0.1 23 | - Initial release; a lot of stuff was broken or not working at all. 24 | - 0.2 25 | - Variance clamping was implemented. 26 | - Finished implementing features present in the UI but did nothing at all in 0.1. 27 | - Fixed a lot of bugs. 28 | - Optimized some stuff. 29 | - 1.0 30 | - New shader that is both simpler and looks cleaner, in my opinion. 31 | - Reworked most parts of the code in some way. 32 | - Removed some features that were never finished or not useful. 33 | - Made the UI much more user-friendly. 34 | - Added comments and docstrings to make it usable by other devs. 35 | - This effect is only one file now for convenience. 36 | - 1.1 37 | - Reordered blending and clamping to be done in the order they are applied in all SOTA methods. 38 | - This dramatically improves the temporal stability of the effect, but also causes a bit more blur. 39 | - Reworked the adaptive sharpening to adjust more accurately to the temporal blur caused by the filter. 40 | - Readded dilated motion vector sampling. 41 | - This reduces artifacts around edges on moving objects. 42 | - Reduced samples used in the sharpening pass to enhance performance; you should not notice a difference. 43 | - 1.1.1 44 | - Reworked the adaptive sharpening and blending weight calculation to use less magic numbers and be more intuitive and look cleaner. 45 | - Removed UI elements that were accidentally left in the shader. 46 | - 1.1.2 47 | - Optimized performance. Now ~ 0.4ms in 4k on a RTX 2070. Before it was ~ 0.6ms. 48 | - Simplified some of the code to make future updates easier. 49 | - Fixed some bugs. 50 | 51 | 52 | # Installation 53 | - Install the current Reshade build. 54 | - Drag the TFAA.fx file into your reshade-shaders/Shaders folder. 55 | - Install https://github.com/martymcmodding/iMMERSE/blob/main/Shaders/MartysMods_LAUNCHPAD.fx. 56 | - Ingame make sure the **depth buffer** is detected correctly by using the DisplayDepth.fx. 57 | - Set the global preprocessor sttings for the depth buffer to match the ones in DisplayDepth.fx. 58 | - Order in Reshade should be ANTIALIASING (SMAA or! CMAA2 or! FXAA) -> LAUNCHPAD -> Any GI/AO/SSR Shaders -> TFAA -> COLOR CORRECTION -> ANYTHING ELSE 59 | 60 | 61 | -------------------------------------------------------------------------------- /TFAA.fx: -------------------------------------------------------------------------------- 1 | /*============================================================================= 2 | TFAA (1.1.2) 3 | Temporal Filter Anti-Aliasing Shader 4 | First published 2022 - Copyright, Jakob Wapenhensch 5 | License: CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/) 6 | https://creativecommons.org/licenses/by-nc/4.0/legalcode 7 | =============================================================================*/ 8 | 9 | #include "ReShadeUI.fxh" 10 | #include "ReShade.fxh" 11 | 12 | /*============================================================================= 13 | Preprocessor Settings 14 | =============================================================================*/ 15 | 16 | // Uniform variable to access the frame time. 17 | uniform float frametime < source = "frametime"; >; 18 | 19 | 20 | /*============================================================================= 21 | UI Uniforms 22 | =============================================================================*/ 23 | 24 | /** 25 | * @brief Slider controlling the strength of the temporal filter. 26 | */ 27 | uniform float UI_TEMPORAL_FILTER_STRENGTH < 28 | ui_type = "slider"; 29 | ui_min = 0.0; 30 | ui_max = 1.0; 31 | ui_step = 0.01; 32 | ui_label = "Temporal Filter Strength"; 33 | ui_category= "Temporal Filter"; 34 | ui_tooltip = ""; 35 | > = 0.5; 36 | 37 | /** 38 | * @brief Slider controlling the amount of adaptive sharpening. 39 | */ 40 | uniform float UI_POST_SHARPEN < 41 | ui_type = "slider"; 42 | ui_min = 0.0; 43 | ui_max = 1.0; 44 | ui_step = 0.01; 45 | ui_label = "Adaptive Sharpening"; 46 | ui_category= "Temporal Filter"; 47 | ui_tooltip = ""; 48 | > = 0.5; 49 | 50 | // uniform int UI_COLOR_CONVERSION_MODE < 51 | // ui_type = "combo"; 52 | // ui_label = "Color Conversion Mode"; 53 | // ui_items = "RGB\0YCbCr\0"; 54 | // ui_tooltip = "Select the color conversion method."; 55 | // ui_category = ""; 56 | // > = 1; 57 | 58 | #define UI_COLOR_CONVERSION_MODE 1 59 | 60 | // uniform int UI_DEBUG_MODE < 61 | // ui_type = "combo"; 62 | // ui_label = "DEBUG MODE"; 63 | // ui_items = "None\0Weight\0Sharp\0Occlusion\0"; 64 | // ui_tooltip = ""; 65 | // ui_category = ""; 66 | // > = 0; 67 | 68 | 69 | /*============================================================================= 70 | Textures & Samplers 71 | =============================================================================*/ 72 | 73 | // Texture and sampler for depth input. 74 | texture texDepthIn : DEPTH; 75 | sampler smpDepthIn { 76 | Texture = texDepthIn; 77 | MipFilter = Linear; 78 | MinFilter = Linear; 79 | MagFilter = Linear; 80 | }; 81 | 82 | // Texture and sampler for the current frame's color. 83 | texture texInCur : COLOR; 84 | sampler smpInCur { 85 | Texture = texInCur; 86 | AddressU = Clamp; 87 | AddressV = Clamp; 88 | MipFilter = Linear; 89 | MinFilter = Linear; 90 | MagFilter = Linear; 91 | }; 92 | 93 | // Backup texture for the current frame's color. 94 | texture texInCurBackup < pooled = true; > { 95 | Width = BUFFER_WIDTH; 96 | Height = BUFFER_HEIGHT; 97 | Format = RGBA8; 98 | }; 99 | 100 | sampler smpInCurBackup { 101 | Texture = texInCurBackup; 102 | AddressU = Clamp; 103 | AddressV = Clamp; 104 | MipFilter = Linear; 105 | MinFilter = Linear; 106 | MagFilter = Linear; 107 | }; 108 | 109 | // Texture for storing the exponential frame buffer. 110 | texture texExpColor < pooled = true; > { 111 | Width = BUFFER_WIDTH; 112 | Height = BUFFER_HEIGHT; 113 | Format = RGBA16F; 114 | }; 115 | 116 | sampler smpExpColor { 117 | Texture = texExpColor; 118 | AddressU = Clamp; 119 | AddressV = Clamp; 120 | MipFilter = Linear; 121 | MinFilter = Linear; 122 | MagFilter = Linear; 123 | }; 124 | 125 | // Backup texture for the exponential frame buffer. 126 | texture texExpColorBackup < pooled = true; > { 127 | Width = BUFFER_WIDTH; 128 | Height = BUFFER_HEIGHT; 129 | Format = RGBA16F; 130 | }; 131 | 132 | sampler smpExpColorBackup { 133 | Texture = texExpColorBackup; 134 | AddressU = Clamp; 135 | AddressV = Clamp; 136 | MipFilter = Linear; 137 | MinFilter = Linear; 138 | MagFilter = Linear; 139 | }; 140 | 141 | // Backup texture for the last frame's depth. 142 | texture texDepthBackup < pooled = true; > { 143 | Width = BUFFER_WIDTH; 144 | Height = BUFFER_HEIGHT; 145 | Format = R16f; 146 | }; 147 | 148 | sampler smpDepthBackup { 149 | Texture = texDepthBackup; 150 | AddressU = Clamp; 151 | AddressV = Clamp; 152 | MipFilter = Point; 153 | MinFilter = Point; 154 | MagFilter = Point; 155 | }; 156 | 157 | /*============================================================================= 158 | Functions 159 | =============================================================================*/ 160 | 161 | /** 162 | * @brief Samples a texture at a specified UV coordinate and mip level. 163 | * 164 | * @param s Sampler reference of the texture. 165 | * @param uv UV coordinate in texture space. 166 | * @param mip Mip level to sample. 167 | * @return The texture sample as a float4. 168 | */ 169 | float4 tex2Dlod(sampler s, float2 uv, float mip) 170 | { 171 | return tex2Dlod(s, float4(uv, 0, mip)); 172 | } 173 | 174 | /** 175 | * @brief Converts an RGB color to the YCbCr color space. 176 | * 177 | * @param rgb Input RGB color. 178 | * @return Corresponding color in YCbCr space (float3). 179 | */ 180 | float3 cvtRgb2YCbCr(float3 rgb) 181 | { 182 | float y = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b; 183 | float cb = (rgb.b - y) * 0.565; 184 | float cr = (rgb.r - y) * 0.713; 185 | 186 | return float3(y, cb, cr); 187 | } 188 | 189 | /** 190 | * @brief Converts a YCbCr color to RGB color space. 191 | * 192 | * @param YCbCr Input color in YCbCr format. 193 | * @return Converted RGB color (float3). 194 | */ 195 | float3 cvtYCbCr2Rgb(float3 YCbCr) 196 | { 197 | return float3( 198 | YCbCr.x + 1.403 * YCbCr.z, 199 | YCbCr.x - 0.344 * YCbCr.y - 0.714 * YCbCr.z, 200 | YCbCr.x + 1.770 * YCbCr.y 201 | ); 202 | } 203 | 204 | /** 205 | * @brief Wrapper function converting RGB to an intermediate color space. 206 | * 207 | * Acts as a pass-through to cvtRgb2YCbCr. 208 | * 209 | * @param rgb Input RGB color. 210 | * @return Converted color in the intermediate space ("whatever" space). 211 | */ 212 | float3 cvtRgb2whatever(float3 rgb) 213 | { 214 | return cvtRgb2YCbCr(rgb); 215 | } 216 | 217 | // float3 cvtRgb2whatever(float3 rgb) 218 | // { 219 | // switch (UI_COLOR_CONVERSION_MODE) 220 | // { 221 | // case 1: // YCbCr 222 | // return cvtRgb2YCbCr(rgb); 223 | // default: // RGB 224 | // return rgb; 225 | // } 226 | // } 227 | 228 | 229 | 230 | /** 231 | * @brief Wrapper function converting the intermediate color space to RGB. 232 | * 233 | * Acts as a pass-through to cvtYCbCr2Rgb. 234 | * 235 | * @param whatever Input color in the intermediate ("whatever") space. 236 | * @return Converted RGB color. 237 | */ 238 | float3 cvtWhatever2Rgb(float3 whatever) 239 | { 240 | return cvtYCbCr2Rgb(whatever); 241 | } 242 | 243 | // float3 cvtWhatever2Rgb(float3 whatever) 244 | // { 245 | // switch (UI_COLOR_CONVERSION_MODE) 246 | // { 247 | // case 1: // YCbCr 248 | // return cvtYCbCr2Rgb(whatever); 249 | // default: // RGB 250 | // return whatever; 251 | // } 252 | // } 253 | 254 | 255 | /** 256 | * @brief Performs bicubic interpolation using 5 sample points. 257 | * 258 | * Inspired by techniques from Marty, this function computes the filtered 259 | * value by calculating sample weights and positions. 260 | * 261 | * @param source Sampler reference of the texture. 262 | * @param texcoord Texture coordinate to be sampled. 263 | * @return Interpolated color as float4. 264 | */ 265 | float4 bicubic_5(sampler source, float2 texcoord) 266 | { 267 | // Compute the texture size. 268 | float2 texsize = tex2Dsize(source); 269 | 270 | // Convert texture coordinate to texel space. 271 | float2 UV = texcoord * texsize; 272 | 273 | // Determine the center of the texel grid. 274 | float2 tc = floor(UV - 0.5) + 0.5; 275 | 276 | // Compute the fractional part for weighting. 277 | float2 f = UV - tc; 278 | 279 | // Calculate powers of f needed for weight computation. 280 | float2 f2 = f * f; 281 | float2 f3 = f2 * f; 282 | 283 | // Compute weights for the neighboring texels. 284 | float2 w0 = f2 - 0.5 * (f3 + f); 285 | float2 w1 = 1.5 * f3 - 2.5 * f2 + 1.0; 286 | float2 w3 = 0.5 * (f3 - f2); 287 | float2 w12 = 1.0 - w0 - w3; 288 | 289 | // Store sample weights and corresponding sample position offsets. 290 | float4 ws[3]; 291 | ws[0].xy = w0; 292 | ws[1].xy = w12; 293 | ws[2].xy = w3; 294 | 295 | // Calculate sample positions in texel space. 296 | ws[0].zw = tc - 1.0; 297 | ws[1].zw = tc + 1.0 - w1 / w12; 298 | ws[2].zw = tc + 2.0; 299 | 300 | // Normalize the sample offsets to texture coordinate space. 301 | ws[0].zw /= texsize; 302 | ws[1].zw /= texsize; 303 | ws[2].zw /= texsize; 304 | 305 | // Combine neighboring samples weighted by the computed factors. 306 | float4 ret; 307 | ret = tex2Dlod(source, float2(ws[1].z, ws[0].w), 0) * ws[1].x * ws[0].y; 308 | ret += tex2Dlod(source, float2(ws[0].z, ws[1].w), 0) * ws[0].x * ws[1].y; 309 | ret += tex2Dlod(source, float2(ws[1].z, ws[1].w), 0) * ws[1].x * ws[1].y; 310 | ret += tex2Dlod(source, float2(ws[2].z, ws[1].w), 0) * ws[2].x * ws[1].y; 311 | ret += tex2Dlod(source, float2(ws[1].z, ws[2].w), 0) * ws[1].x * ws[2].y; 312 | 313 | // Normalize the result. 314 | float normfact = 1.0 / (1.0 - (f.x - f2.x) * (f.y - f2.y) * 0.25); 315 | return max(0, ret * normfact); 316 | } 317 | 318 | /** 319 | * @brief Samples historical frame data using bicubic interpolation. 320 | * 321 | * Wraps the bicubic interpolation method to retrieve a filtered history value. 322 | * 323 | * @param historySampler Sampler for the history texture. 324 | * @param texcoord Texture coordinate. 325 | * @return Filtered historical sample as a float4. 326 | */ 327 | float4 sampleHistory(sampler2D historySampler, float2 texcoord) 328 | { 329 | return bicubic_5(historySampler, texcoord); 330 | } 331 | 332 | /** 333 | * @brief Retrieves and linearizes the depth value from the depth texture. 334 | * 335 | * Converts the non-linear depth sample into a linear depth value and handles 336 | * reversed depth input when enabled. 337 | * 338 | * @param texcoord Texture coordinate. 339 | * @return Linearized depth value. 340 | */ 341 | float getDepth(float2 texcoord) 342 | { 343 | // Sample raw depth. 344 | float depth = tex2Dlod(smpDepthIn, texcoord, 0).x; 345 | 346 | #if RESHADE_DEPTH_INPUT_IS_REVERSED 347 | // Adjust for reversed depth if required. 348 | depth = 1.0 - depth; 349 | #endif 350 | 351 | // Define a near plane constant. 352 | const float N = 1.0; 353 | 354 | float factor = RESHADE_DEPTH_LINEARIZATION_FAR_PLANE * 0.1; 355 | 356 | // Linearize depth based on the far plane parameter. 357 | depth /= factor - depth * (factor - N); 358 | 359 | return depth; 360 | } 361 | 362 | 363 | /*============================================================================= 364 | Motion Vector Imports 365 | =============================================================================*/ 366 | 367 | namespace Deferred 368 | { 369 | // Texture storing motion vectors (RGBA16F). 370 | // XY: Delta UV; Z: Confidence; W: Depth. 371 | texture MotionVectorsTex { 372 | Width = BUFFER_WIDTH; 373 | Height = BUFFER_HEIGHT; 374 | Format = RG16F; 375 | }; 376 | sampler sMotionVectorsTex { 377 | Texture = MotionVectorsTex; 378 | }; 379 | 380 | /** 381 | * @brief Retrieves the motion vector at a given texture coordinate. 382 | * 383 | * @param uv Texture coordinate. 384 | * @return Motion vector as a float2. 385 | */ 386 | float2 get_motion(float2 uv) 387 | { 388 | return tex2Dlod(sMotionVectorsTex, uv, 0).xy; 389 | } 390 | } 391 | 392 | 393 | /*============================================================================= 394 | Shader Pass Functions 395 | =============================================================================*/ 396 | 397 | /** 398 | * @brief Saves the current frame's color and depth into a backup texture. 399 | * 400 | * Samples the scene's color and computes the linearized depth for use in 401 | * later temporal filtering passes. 402 | * 403 | * @param position Unused screen-space position. 404 | * @param texcoord Texture coordinate. 405 | * @return Color from the current frame with depth stored in the alpha channel. 406 | */ 407 | float4 PassSaveCur(float4 position : SV_Position, float2 texcoord : TEXCOORD) : SV_Target0 408 | { 409 | // Retrieve and linearize depth. 410 | float depthOnly = getDepth(texcoord); 411 | 412 | // Sample current frame color and pack depth into alpha channel. 413 | return float4(tex2Dlod(smpInCur, texcoord, 0).rgb, depthOnly); 414 | } 415 | 416 | /** 417 | * @brief Applies the temporal filter for anti-aliasing. 418 | * 419 | * Blends the current frame with historical data based on motion vectors, 420 | * local contrast, and depth continuity. This minimizes aliasing artifacts 421 | * while also calculating the amount of adaptive sharpening. 422 | * 423 | * @param position Unused screen-space position. 424 | 425 | * @param texcoord Texture coordinate. 426 | * @return Processed color after temporal filtering + sharpening factor. 427 | */ 428 | float4 PassTemporalFilter(float4 position : SV_Position, float2 texcoord : TEXCOORD) : SV_Target 429 | { 430 | // Sample current frame. 431 | float4 sampleCur = tex2Dlod(smpInCurBackup, texcoord, 0); 432 | float4 cvtColorCur = float4(cvtRgb2whatever(sampleCur.rgb), sampleCur.a); 433 | 434 | static const int samples = 9; 435 | 436 | // Offsets for a 3x3 neighborhood (center is at index 4). 437 | static const float2 nOffsets[samples] = { 438 | float2(-0.7,-0.7), float2(0, 1), float2(0.7, 0.7), 439 | float2(-1, 0), float2(0, 0), float2(1, 0), 440 | float2(-0.7, 0.7), float2(0, -1), float2(0.7, 0.7) 441 | }; 442 | 443 | 444 | // The center of the neighborhood (index 4) is assumed to have the closest depth. 445 | int closestDepthIndex = 4; 446 | 447 | // Initialize min/max bounds 448 | float4 minimumCvt = 2; 449 | float4 maximumCvt = -1; 450 | 451 | // Sample the neighborhood and update min/max colors in clamping color space. 452 | for (int i = 0; i < samples; i++) 453 | { 454 | float4 rgba = tex2Dlod(smpInCurBackup, texcoord + (nOffsets[i] * ReShade::PixelSize), 0); 455 | float4 cvt = float4(cvtRgb2whatever(rgba.rgb), rgba.a); 456 | 457 | if (rgba.a < minimumCvt.a) 458 | closestDepthIndex = i; 459 | 460 | minimumCvt = min(minimumCvt, cvt); 461 | maximumCvt = max(maximumCvt, cvt); 462 | } 463 | 464 | // Retrieve dilated motion vector. 465 | float2 motion = Deferred::get_motion(texcoord + (nOffsets[closestDepthIndex] * ReShade::PixelSize)); 466 | 467 | // Compute the corresponding sample position from the previous frame. 468 | float2 lastSamplePos = texcoord + motion; 469 | 470 | // Sample exponential color and last depth. 471 | float4 sampleExp = saturate(sampleHistory(smpExpColorBackup, lastSamplePos)); 472 | float lastDepth = tex2Dlod(smpDepthBackup, lastSamplePos, 0).r; 473 | 474 | 475 | // Compute temporal weight factors 476 | float localContrast= saturate(pow(length(maximumCvt.rgb - minimumCvt.rgb), 0.75)); 477 | float speed = length(motion.xy); 478 | float speedFactor = 1.0 - pow(saturate(speed * 50), 0.75); 479 | 480 | // Calculate the depth difference and construct a disocclusion mask. 481 | float depthDelta = saturate(minimumCvt.a - lastDepth); 482 | depthDelta = saturate(pow(depthDelta, 4) - 0.0000001); 483 | float depthMask = 1.0 - (depthDelta * 10000000); 484 | 485 | // Compute the blending weight. 486 | float weight = lerp(0.5, 0.99, pow(UI_TEMPORAL_FILTER_STRENGTH, 0.5)); 487 | weight = clamp(weight * speedFactor * saturate(localContrast + 0.75) * depthMask, 0.0, 0.99); 488 | 489 | // This factor is used to weight bright pixels more during blending, preserving highlights. 490 | const static float correctionFactor = 2; 491 | 492 | // The actual blending of the old exponential color with the new one. 493 | float4 blendedColor = saturate(pow(lerp(pow(sampleCur, correctionFactor), pow(sampleExp, correctionFactor), weight), (1.0 / correctionFactor))); 494 | 495 | // This converts the blended color to the intermediate color space. 496 | blendedColor = float4(cvtRgb2whatever(blendedColor.rgb), blendedColor.a); 497 | 498 | // Clamp the blended color to the local neighborhood bounds. 499 | blendedColor = float4(clamp(blendedColor.rgb, minimumCvt.rgb, maximumCvt.rgb), blendedColor.a); 500 | 501 | // Convert the blended color back to RGB. 502 | blendedColor = float4(cvtWhatever2Rgb(blendedColor.rgb), blendedColor.a); 503 | 504 | //Calulate Sharpeninbg factor. 505 | float sharp = saturate(UI_POST_SHARPEN * UI_TEMPORAL_FILTER_STRENGTH * pow(speed * 2048, 0.5) * localContrast * depthMask); 506 | 507 | 508 | 509 | float3 return_value = blendedColor.rgb; 510 | // switch (UI_DEBUG_MODE) 511 | // { 512 | // case 1: 513 | // return_value = weight; 514 | // break; 515 | // case 2: 516 | // return_value = sharp; 517 | // break; 518 | // case 3: 519 | // return_value = depthMask; 520 | // break; 521 | // default: 522 | // break; 523 | // } 524 | 525 | // Return the final blended color and sharpening factor. 526 | return float4(return_value, sharp); 527 | } 528 | 529 | /** 530 | * @brief Saves the post-processed exponential color and depth for history. 531 | * 532 | * This pass stores the final exponential color buffer and the corresponding 533 | * linearized depth value for usage in subsequent frames. 534 | * 535 | * @param position Unused screen-space position. 536 | * @param texcoord Texture coordinate. 537 | * @param lastExpOut Output exponential color buffer. 538 | * @param depthOnly Output linearized depth. 539 | */ 540 | void PassSavePost(float4 position : SV_Position, float2 texcoord : TEXCOORD, out float4 lastExpOut : SV_Target0, out float depthOnly : SV_Target1) 541 | { 542 | // Store the current exponential color. 543 | lastExpOut = tex2Dlod(smpExpColor, texcoord, 0); 544 | 545 | // Store the corresponding linearized depth. 546 | depthOnly = getDepth(texcoord); 547 | } 548 | 549 | /** 550 | * @brief Final output pass that applies adaptive sharpening. 551 | * 552 | * Applies adaptive sharpening to the final image. 553 | * 554 | * @param position Unused screen-space position. 555 | * @param texcoord Texture coordinate. 556 | * @return The final processed color with sharpening applied. 557 | */ 558 | float4 PassSharp(float4 position : SV_Position, float2 texcoord : TEXCOORD ) : SV_Target 559 | { 560 | // Sample the center and neighboring pixels. 561 | float4 center = tex2Dlod(smpExpColor, texcoord, 0); 562 | float4 top = tex2Dlod(smpExpColor, texcoord + (float2(0, -1) * ReShade::PixelSize), 0); 563 | float4 bottom = tex2Dlod(smpExpColor, texcoord + (float2(0, 1) * ReShade::PixelSize), 0); 564 | float4 left = tex2Dlod(smpExpColor, texcoord + (float2(-1, 0) * ReShade::PixelSize), 0); 565 | float4 right = tex2Dlod(smpExpColor, texcoord + (float2(1, 0) * ReShade::PixelSize), 0); 566 | 567 | // Find the maximum and minimum among the sampled neighbors. 568 | float4 maxBox = max(top, max(bottom, max(left, max(right, center)))); 569 | float4 minBox = min(top, min(bottom, min(left, min(right, center)))); 570 | 571 | // Fixed contrast value (tuned for high temporal blur scenarios). 572 | float contrast = 0.8; 573 | float sharpAmount = saturate(maxBox.a * 10); 574 | 575 | // Calculate cross weights similarly to AMD CAS. 576 | float4 crossWeight = -rcp(rsqrt(saturate(min(minBox, 1.0 - maxBox) * rcp(maxBox))) * (-3.0 * contrast + 8.0)); 577 | 578 | // Compute reciprocal weight based on the sum of the cross weights. 579 | float4 rcpWeight = rcp(4.0 * crossWeight + 1.0); 580 | 581 | // Sum the direct neighbors (top, bottom, left, right). 582 | float4 crossSumm = top + bottom + left + right; 583 | 584 | // Combine center pixel with weighted neighbors. 585 | return lerp(center, saturate((crossSumm * crossWeight + center) * rcpWeight), sharpAmount); 586 | 587 | } 588 | 589 | 590 | /*============================================================================= 591 | Shader Technique: TFAA 592 | =============================================================================*/ 593 | 594 | /** 595 | * @brief Temporal Filter Anti-Aliasing Technique. 596 | * 597 | * The technique is composed of the following passes: 598 | * - PassSavePre: Saves the current frame's color and depth. 599 | * - PassTemporalFilter: Applies temporal filtering using history and motion vectors. 600 | * - PassSavePost: Stores the exponential color buffer and depth for history. 601 | * - PassShow: Outputs the final image with adaptive sharpening. 602 | */ 603 | technique TFAA 604 | < 605 | ui_label = "TFAA"; 606 | ui_tooltip = "- Temporal Filter Anti-Aliasing -\nTemporal component of TAA to be used with (after) spatial anti-aliasing techniques.\nRequires motion vectors to be available (LAUNCHPAD.fx)."; 607 | > 608 | { 609 | pass PassSavePre 610 | { 611 | VertexShader = PostProcessVS; 612 | PixelShader = PassSaveCur; 613 | RenderTarget = texInCurBackup; 614 | } 615 | 616 | pass PassTemporalFilter 617 | { 618 | VertexShader = PostProcessVS; 619 | PixelShader = PassTemporalFilter; 620 | RenderTarget = texExpColor; 621 | } 622 | 623 | pass PassSavePost 624 | { 625 | VertexShader = PostProcessVS; 626 | PixelShader = PassSavePost; 627 | RenderTarget0 = texExpColorBackup; 628 | RenderTarget1 = texDepthBackup; 629 | } 630 | 631 | pass PassShow 632 | { 633 | VertexShader = PostProcessVS; 634 | PixelShader = PassSharp; 635 | } 636 | } --------------------------------------------------------------------------------