// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' // Copyright (c) <2015> // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE.TXT) // AUTHOR: Lasse Jon Fuglsang Pedersen Shader "Hidden/TAA" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } CGINCLUDE //--- program begin #pragma only_renderers ps4 xboxone d3d11 d3d9 xbox360 opengl glcore gles3 metal vulkan #pragma target 3.0 #pragma multi_compile CAMERA_PERSPECTIVE CAMERA_ORTHOGRAPHIC #pragma multi_compile MINMAX_3X3 MINMAX_3X3_ROUNDED MINMAX_4TAP_VARYING #pragma multi_compile __ UNJITTER_COLORSAMPLES #pragma multi_compile __ UNJITTER_NEIGHBORHOOD #pragma multi_compile __ UNJITTER_REPROJECTION #pragma multi_compile __ USE_YCOCG #pragma multi_compile __ USE_CLIPPING #pragma multi_compile __ USE_DILATION #pragma multi_compile __ USE_MOTION_BLUR #pragma multi_compile __ USE_MOTION_BLUR_NEIGHBORMAX #pragma multi_compile __ USE_OPTIMIZATIONS #include "UnityCG.cginc" #include "IncDepth.cginc" #include "IncNoise.cginc" #if SHADER_API_MOBILE static const float FLT_EPS = 0.0001f; #else static const float FLT_EPS = 0.00000001f; #endif uniform float4 _JitterUV;// frustum jitter uv deltas, where xy = current frame, zw = previous uniform sampler2D _MainTex; uniform float4 _MainTex_TexelSize; uniform sampler2D_half _VelocityBuffer; uniform sampler2D _VelocityNeighborMax; uniform sampler2D _PrevTex; uniform float _FeedbackMin; uniform float _FeedbackMax; uniform float _MotionScale; struct v2f { float4 cs_pos : SV_POSITION; float2 ss_txc : TEXCOORD0; }; v2f vert(appdata_img IN) { v2f OUT; #if UNITY_VERSION < 540 OUT.cs_pos = UnityObjectToClipPos(IN.vertex); #else OUT.cs_pos = UnityObjectToClipPos(IN.vertex); #endif #if UNITY_SINGLE_PASS_STEREO OUT.ss_txc = UnityStereoTransformScreenSpaceTex(IN.texcoord.xy); #else OUT.ss_txc = IN.texcoord.xy; #endif return OUT; } // https://software.intel.com/en-us/node/503873 float3 RGB_YCoCg(float3 c) { // Y = R/4 + G/2 + B/4 // Co = R/2 - B/2 // Cg = -R/4 + G/2 - B/4 return float3( c.x/4.0 + c.y/2.0 + c.z/4.0, c.x/2.0 - c.z/2.0, -c.x/4.0 + c.y/2.0 - c.z/4.0 ); } // https://software.intel.com/en-us/node/503873 float3 YCoCg_RGB(float3 c) { // R = Y + Co - Cg // G = Y + Cg // B = Y - Co - Cg return saturate(float3( c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z )); } float4 sample_color(sampler2D tex, float2 uv) { #if USE_YCOCG float4 c = tex2D(tex, uv); return float4(RGB_YCoCg(c.rgb), c.a); #else return tex2D(tex, uv); #endif } float4 resolve_color(float4 c) { #if USE_YCOCG return float4(YCoCg_RGB(c.rgb).rgb, c.a); #else return c; #endif } float4 clip_aabb(float3 aabb_min, float3 aabb_max, float4 p, float4 q) { #if USE_OPTIMIZATIONS // note: only clips towards aabb center (but fast!) float3 p_clip = 0.5 * (aabb_max + aabb_min); float3 e_clip = 0.5 * (aabb_max - aabb_min) + FLT_EPS; float4 v_clip = q - float4(p_clip, p.w); float3 v_unit = v_clip.xyz / e_clip; float3 a_unit = abs(v_unit); float ma_unit = max(a_unit.x, max(a_unit.y, a_unit.z)); if (ma_unit > 1.0) return float4(p_clip, p.w) + v_clip / ma_unit; else return q;// point inside aabb #else float4 r = q - p; float3 rmax = aabb_max - p.xyz; float3 rmin = aabb_min - p.xyz; const float eps = FLT_EPS; if (r.x > rmax.x + eps) r *= (rmax.x / r.x); if (r.y > rmax.y + eps) r *= (rmax.y / r.y); if (r.z > rmax.z + eps) r *= (rmax.z / r.z); if (r.x < rmin.x - eps) r *= (rmin.x / r.x); if (r.y < rmin.y - eps) r *= (rmin.y / r.y); if (r.z < rmin.z - eps) r *= (rmin.z / r.z); return p + r; #endif } float2 sample_velocity_dilated(sampler2D tex, float2 uv, int support) { float2 du = float2(_MainTex_TexelSize.x, 0.0); float2 dv = float2(0.0, _MainTex_TexelSize.y); float2 mv = 0.0; float rmv = 0.0; int end = support + 1; for (int i = -support; i != end; i++) { for (int j = -support; j != end; j++) { float2 v = tex2D(tex, uv + i * dv + j * du).xy; float rv = dot(v, v); if (rv > rmv) { mv = v; rmv = rv; } } } return mv; } float4 sample_color_motion(sampler2D tex, float2 uv, float2 ss_vel) { const float2 v = 0.5 * ss_vel; const int taps = 3;// on either side! float srand = PDsrand(uv + _SinTime.xx); float2 vtap = v / taps; float2 pos0 = uv + vtap * (0.5 * srand); float4 accu = 0.0; float wsum = 0.0; [unroll] for (int i = -taps; i <= taps; i++) { float w = 1.0;// box //float w = taps - abs(i) + 1;// triangle //float w = 1.0 / (1 + abs(i));// pointy triangle accu += w * sample_color(tex, pos0 + i * vtap); wsum += w; } return accu / wsum; } float4 temporal_reprojection(float2 ss_txc, float2 ss_vel, float vs_dist) { // read texels #if UNJITTER_COLORSAMPLES float4 texel0 = sample_color(_MainTex, ss_txc - _JitterUV.xy); #else float4 texel0 = sample_color(_MainTex, ss_txc); #endif float4 texel1 = sample_color(_PrevTex, ss_txc - ss_vel); // calc min-max of current neighbourhood #if UNJITTER_NEIGHBORHOOD float2 uv = ss_txc - _JitterUV.xy; #else float2 uv = ss_txc; #endif #if MINMAX_3X3 || MINMAX_3X3_ROUNDED float2 du = float2(_MainTex_TexelSize.x, 0.0); float2 dv = float2(0.0, _MainTex_TexelSize.y); float4 ctl = sample_color(_MainTex, uv - dv - du); float4 ctc = sample_color(_MainTex, uv - dv); float4 ctr = sample_color(_MainTex, uv - dv + du); float4 cml = sample_color(_MainTex, uv - du); float4 cmc = sample_color(_MainTex, uv); float4 cmr = sample_color(_MainTex, uv + du); float4 cbl = sample_color(_MainTex, uv + dv - du); float4 cbc = sample_color(_MainTex, uv + dv); float4 cbr = sample_color(_MainTex, uv + dv + du); float4 cmin = min(ctl, min(ctc, min(ctr, min(cml, min(cmc, min(cmr, min(cbl, min(cbc, cbr)))))))); float4 cmax = max(ctl, max(ctc, max(ctr, max(cml, max(cmc, max(cmr, max(cbl, max(cbc, cbr)))))))); #if MINMAX_3X3_ROUNDED || USE_YCOCG || USE_CLIPPING float4 cavg = (ctl + ctc + ctr + cml + cmc + cmr + cbl + cbc + cbr) / 9.0; #endif #if MINMAX_3X3_ROUNDED float4 cmin5 = min(ctc, min(cml, min(cmc, min(cmr, cbc)))); float4 cmax5 = max(ctc, max(cml, max(cmc, max(cmr, cbc)))); float4 cavg5 = (ctc + cml + cmc + cmr + cbc) / 5.0; cmin = 0.5 * (cmin + cmin5); cmax = 0.5 * (cmax + cmax5); cavg = 0.5 * (cavg + cavg5); #endif #elif MINMAX_4TAP_VARYING// this is the method used in v2 (PDTemporalReprojection2) const float _SubpixelThreshold = 0.5; const float _GatherBase = 0.5; const float _GatherSubpixelMotion = 0.1666; float2 texel_vel = ss_vel / _MainTex_TexelSize.xy; float texel_vel_mag = length(texel_vel) * vs_dist; float k_subpixel_motion = saturate(_SubpixelThreshold / (FLT_EPS + texel_vel_mag)); float k_min_max_support = _GatherBase + _GatherSubpixelMotion * k_subpixel_motion; float2 ss_offset01 = k_min_max_support * float2(-_MainTex_TexelSize.x, _MainTex_TexelSize.y); float2 ss_offset11 = k_min_max_support * float2(_MainTex_TexelSize.x, _MainTex_TexelSize.y); float4 c00 = sample_color(_MainTex, uv - ss_offset11); float4 c10 = sample_color(_MainTex, uv - ss_offset01); float4 c01 = sample_color(_MainTex, uv + ss_offset01); float4 c11 = sample_color(_MainTex, uv + ss_offset11); float4 cmin = min(c00, min(c10, min(c01, c11))); float4 cmax = max(c00, max(c10, max(c01, c11))); #if USE_YCOCG || USE_CLIPPING float4 cavg = (c00 + c10 + c01 + c11) / 4.0; #endif #else #error "missing keyword MINMAX_..." #endif // shrink chroma min-max #if USE_YCOCG float2 chroma_extent = 0.25 * 0.5 * (cmax.r - cmin.r); float2 chroma_center = texel0.gb; cmin.yz = chroma_center - chroma_extent; cmax.yz = chroma_center + chroma_extent; cavg.yz = chroma_center; #endif // clamp to neighbourhood of current sample #if USE_CLIPPING texel1 = clip_aabb(cmin.xyz, cmax.xyz, clamp(cavg, cmin, cmax), texel1); #else texel1 = clamp(texel1, cmin, cmax); #endif // feedback weight from unbiased luminance diff (t.lottes) #if USE_YCOCG float lum0 = texel0.r; float lum1 = texel1.r; #else float lum0 = Luminance(texel0.rgb); float lum1 = Luminance(texel1.rgb); #endif float unbiased_diff = abs(lum0 - lum1) / max(lum0, max(lum1, 0.2)); float unbiased_weight = 1.0 - unbiased_diff; float unbiased_weight_sqr = unbiased_weight * unbiased_weight; float k_feedback = lerp(_FeedbackMin, _FeedbackMax, unbiased_weight_sqr); // output return lerp(texel0, texel1, k_feedback); } struct f2rt { fixed4 buffer : SV_Target0; fixed4 screen : SV_Target1; }; f2rt frag(v2f IN) { f2rt OUT; #if UNJITTER_REPROJECTION float2 uv = IN.ss_txc - _JitterUV.xy; #else float2 uv = IN.ss_txc; #endif #if USE_DILATION //--- 3x3 norm (sucks) //float2 ss_vel = sample_velocity_dilated(_VelocityBuffer, uv, 1); //float vs_dist = depth_sample_linear(uv); //--- 5 tap nearest (decent) //float3 c_frag = find_closest_fragment_5tap(uv); //float2 ss_vel = tex2D(_VelocityBuffer, c_frag.xy).xy; //float vs_dist = depth_resolve_linear(c_frag.z); //--- 3x3 nearest (good) float3 c_frag = find_closest_fragment_3x3(uv); float2 ss_vel = tex2D(_VelocityBuffer, c_frag.xy).xy; float vs_dist = depth_resolve_linear(c_frag.z); #else float2 ss_vel = tex2D(_VelocityBuffer, uv).xy; float vs_dist = depth_sample_linear(uv); #endif // temporal resolve float4 color_temporal = temporal_reprojection(IN.ss_txc, ss_vel, vs_dist); // prepare outputs float4 to_buffer = resolve_color(color_temporal); #if USE_MOTION_BLUR #if USE_MOTION_BLUR_NEIGHBORMAX ss_vel = _MotionScale * tex2D(_VelocityNeighborMax, IN.ss_txc).xy; #else ss_vel = _MotionScale * ss_vel; #endif float vel_mag = length(ss_vel * _MainTex_TexelSize.zw); const float vel_trust_full = 2.0; const float vel_trust_none = 15.0; const float vel_trust_span = vel_trust_none - vel_trust_full; float trust = 1.0 - clamp(vel_mag - vel_trust_full, 0.0, vel_trust_span) / vel_trust_span; #if UNJITTER_COLORSAMPLES float4 color_motion = sample_color_motion(_MainTex, IN.ss_txc - _JitterUV.xy, ss_vel); #else float4 color_motion = sample_color_motion(_MainTex, IN.ss_txc, ss_vel); #endif float4 to_screen = resolve_color(lerp(color_motion, color_temporal, trust)); #else float4 to_screen = resolve_color(color_temporal); #endif //// NOTE: velocity debug //to_screen.g += 100.0 * length(ss_vel); //to_screen = float4(100.0 * abs(ss_vel), 0.0, 0.0); // add noise float4 noise4 = PDsrand4(IN.ss_txc + _SinTime.x + 0.6959174) / 510.0; OUT.buffer = saturate(to_buffer /*+ noise4*/); //OUT.screen = saturate(to_screen + noise4); OUT.screen = saturate(to_screen /*+ noise4*/)/* * float4(1,.2,0,1)*/; //OUT.screen = float4(vs_dist, vs_dist, vs_dist,1); // done return OUT; } //--- program end ENDCG SubShader { ZTest Always Cull Off ZWrite Off Fog { Mode off } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } Fallback off }