You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

423 lines
11 KiB

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
// Copyright (c) <2015> <Playdead>
// This file is subject to the MIT License as seen in the root of this folder structure (LICENSE.TXT)
// AUTHOR: Lasse Jon Fuglsang Pedersen <lasse@playdead.com>
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
}