#ifndef __TAA__ #define __TAA__ #pragma only_renderers ps4 xboxone d3d11 d3d9 xbox360 opengl glcore #pragma exclude_renderers gles #include "UnityCG.cginc" #include "Common.cginc" // ----------------------------------------------------------------------------- // Solver #define TAA_USE_STABLE_BUT_GHOSTY_VARIANT 0 #if !defined(TAA_DILATE_MOTION_VECTOR_SAMPLE) #define TAA_DILATE_MOTION_VECTOR_SAMPLE 1 #endif #define TAA_FRAGMENT_MOTION_HISTORY_DECAY 0.85 #define TAA_FINAL_BLEND_STATIC_FACTOR _FinalBlendParameters.x #define TAA_FINAL_BLEND_DYNAMIC_FACTOR _FinalBlendParameters.y #define TAA_MOTION_AMPLIFICATION _FinalBlendParameters.z struct VaryingsSolver { float4 vertex : SV_POSITION; float4 uv : TEXCOORD0; // [xy: _MainTex.uv, zw: _HistoryTex.uv] }; struct OutputSolver { float4 destination : SV_Target0; float4 history : SV_Target1; }; sampler2D _HistoryTex; sampler2D _CameraMotionVectorsTexture; sampler2D _CameraDepthTexture; float4 _HistoryTex_TexelSize; float4 _CameraDepthTexture_TexelSize; float2 _Jitter; float4 _SharpenParameters; float4 _FinalBlendParameters; VaryingsSolver VertSolver(AttributesDefault input) { VaryingsSolver output; float4 vertex = UnityObjectToClipPos(input.vertex); output.vertex = vertex; output.uv = input.texcoord.xyxy; #if UNITY_UV_STARTS_AT_TOP if (_MainTex_TexelSize.y < 0) output.uv.y = 1.0 - input.texcoord.y; #endif return output; } float2 GetClosestFragment(float2 uv) { const float2 k = _CameraDepthTexture_TexelSize.xy; const float4 neighborhood = float4( SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv - k), SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv + float2(k.x, -k.y)), SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv + float2(-k.x, k.y)), SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv + k) ); #if defined(UNITY_REVERSED_Z) #define COMPARE_DEPTH(a, b) step(b, a) #else #define COMPARE_DEPTH(a, b) step(a, b) #endif float3 result = float3(0.0, 0.0, SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv)); result = lerp(result, float3(-1.0, -1.0, neighborhood.x), COMPARE_DEPTH(neighborhood.x, result.z)); result = lerp(result, float3( 1.0, -1.0, neighborhood.y), COMPARE_DEPTH(neighborhood.y, result.z)); result = lerp(result, float3(-1.0, 1.0, neighborhood.z), COMPARE_DEPTH(neighborhood.z, result.z)); result = lerp(result, float3( 1.0, 1.0, neighborhood.w), COMPARE_DEPTH(neighborhood.w, result.z)); return (uv + result.xy * k); } // Adapted from Playdead's TAA implementation // https://github.com/playdeadgames/temporal float4 ClipToAABB(float4 color, float p, float3 minimum, float3 maximum) { // note: only clips towards aabb center (but fast!) float3 center = 0.5 * (maximum + minimum); float3 extents = 0.5 * (maximum - minimum); // This is actually `distance`, however the keyword is reserved float4 offset = color - float4(center, p); float3 repeat = abs(offset.xyz / extents); repeat.x = max(repeat.x, max(repeat.y, repeat.z)); if (repeat.x > 1.0) { // `color` is not intersecting (nor inside) the AABB; it's clipped to the closest extent return float4(center, p) + offset / repeat.x; } else { // `color` is intersecting (or inside) the AABB. // Note: for whatever reason moving this return statement from this else into a higher // scope makes the NVIDIA drivers go beyond bonkers return color; } } OutputSolver FragSolver(VaryingsSolver input) { #if TAA_DILATE_MOTION_VECTOR_SAMPLE float2 motion = tex2D(_CameraMotionVectorsTexture, GetClosestFragment(input.uv.zw)).xy; #else // Don't dilate in ortho ! float2 motion = tex2D(_CameraMotionVectorsTexture, input.uv.zw).xy; #endif const float2 k = _MainTex_TexelSize.xy; float2 uv = input.uv.xy; #if UNITY_UV_STARTS_AT_TOP uv -= _MainTex_TexelSize.y < 0 ? _Jitter * float2(1.0, -1.0) : _Jitter; #else uv -= _Jitter; #endif float4 color = tex2D(_MainTex, uv); float4 topLeft = tex2D(_MainTex, uv - k * 0.5); float4 bottomRight = tex2D(_MainTex, uv + k * 0.5); float4 corners = 4.0 * (topLeft + bottomRight) - 2.0 * color; // Sharpen output color += (color - (corners * 0.166667)) * 2.718282 * _SharpenParameters.x; color = max(0.0, color); // Tonemap color and history samples float4 average = FastToneMap((corners + color) * 0.142857); topLeft = FastToneMap(topLeft); bottomRight = FastToneMap(bottomRight); color = FastToneMap(color); float4 history = tex2D(_HistoryTex, input.uv.zw - motion); // Only use this variant for arch viz or scenes that don't have any animated objects (camera animation is fine) #if TAA_USE_STABLE_BUT_GHOSTY_VARIANT float4 luma = float4(Luminance(topLeft.rgb), Luminance(bottomRight.rgb), Luminance(average.rgb), Luminance(color.rgb)); float nudge = lerp(6.28318530718, 0.5, saturate(2.0 * history.a)) * max(abs(luma.z - luma.w), abs(luma.x - luma.y)); float4 minimum = lerp(bottomRight, topLeft, step(luma.x, luma.y)) - nudge; float4 maximum = lerp(topLeft, bottomRight, step(luma.x, luma.y)) + nudge; #else float2 luma = float2(Luminance(average.rgb), Luminance(color.rgb)); float nudge = 4.0 * abs(luma.x - luma.y); float4 minimum = min(bottomRight, topLeft) - nudge; float4 maximum = max(topLeft, bottomRight) + nudge; #endif history = FastToneMap(history); // Clip history samples history = ClipToAABB(history, history.a, minimum.xyz, maximum.xyz); // Store fragment motion history color.a = saturate(smoothstep(0.002 * _MainTex_TexelSize.z, 0.0035 * _MainTex_TexelSize.z, length(motion))); // Blend method float weight = clamp(lerp(TAA_FINAL_BLEND_STATIC_FACTOR, TAA_FINAL_BLEND_DYNAMIC_FACTOR, length(motion) * TAA_MOTION_AMPLIFICATION), TAA_FINAL_BLEND_DYNAMIC_FACTOR, TAA_FINAL_BLEND_STATIC_FACTOR); color = FastToneUnmap(lerp(color, history, weight)); OutputSolver output; output.destination = color; color.a *= TAA_FRAGMENT_MOTION_HISTORY_DECAY; output.history = color; return output; } // ----------------------------------------------------------------------------- // Alpha clearance float4 FragAlphaClear(VaryingsDefault input) : SV_Target { return float4(tex2D(_MainTex, input.uv).rgb, 0.0); } #endif // __TAA__