1 Shader "Hidden/PostProcessing/MotionBlur"
6 #include "../StdLib.hlsl"
8 TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
9 float4 _MainTex_TexelSize;
11 // Camera depth texture
12 TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture);
14 // Camera motion vectors texture
15 TEXTURE2D_SAMPLER2D(_CameraMotionVectorsTexture, sampler_CameraMotionVectorsTexture);
16 float4 _CameraMotionVectorsTexture_TexelSize;
18 // Packed velocity texture (2/10/10/10)
19 TEXTURE2D_SAMPLER2D(_VelocityTex, sampler_VelocityTex);
20 float2 _VelocityTex_TexelSize;
22 // NeighborMax texture
23 TEXTURE2D_SAMPLER2D(_NeighborMaxTex, sampler_NeighborMaxTex);
24 float2 _NeighborMaxTex_TexelSize;
26 // Velocity scale factor
29 // TileMax filter parameters
33 // Maximum blur radius (in pixels)
35 float _RcpMaxBlurRadius;
37 // Filter parameters/coefficients
40 // -----------------------------------------------------------------------------
43 // Velocity texture setup
44 half4 FragVelocitySetup(VaryingsDefault i) : SV_Target
46 // Sample the motion vector.
47 float2 v = SAMPLE_TEXTURE2D(_CameraMotionVectorsTexture, sampler_CameraMotionVectorsTexture, i.texcoord).rg;
49 // Apply the exposure time and convert to the pixel space.
50 v *= (_VelocityScale * 0.5) * _CameraMotionVectorsTexture_TexelSize.zw;
52 // Clamp the vector with the maximum blur radius.
53 v /= max(1.0, length(v) * _RcpMaxBlurRadius);
55 // Sample the depth of the pixel.
56 half d = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.texcoord));
58 // Pack into 10/10/10/2 format.
59 return half4((v * _RcpMaxBlurRadius + 1.0) * 0.5, d, 0.0);
62 half2 MaxV(half2 v1, half2 v2)
64 return dot(v1, v1) < dot(v2, v2) ? v2 : v1;
67 // TileMax filter (2 pixel width with normalization)
68 half4 FragTileMax1(VaryingsDefault i) : SV_Target
70 float4 d = _MainTex_TexelSize.xyxy * float4(-0.5, -0.5, 0.5, 0.5);
72 half2 v1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.xy).rg;
73 half2 v2 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.zy).rg;
74 half2 v3 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.xw).rg;
75 half2 v4 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.zw).rg;
77 v1 = (v1 * 2.0 - 1.0) * _MaxBlurRadius;
78 v2 = (v2 * 2.0 - 1.0) * _MaxBlurRadius;
79 v3 = (v3 * 2.0 - 1.0) * _MaxBlurRadius;
80 v4 = (v4 * 2.0 - 1.0) * _MaxBlurRadius;
82 return half4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
85 // TileMax filter (2 pixel width)
86 half4 FragTileMax2(VaryingsDefault i) : SV_Target
88 float4 d = _MainTex_TexelSize.xyxy * float4(-0.5, -0.5, 0.5, 0.5);
90 half2 v1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.xy).rg;
91 half2 v2 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.zy).rg;
92 half2 v3 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.xw).rg;
93 half2 v4 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.zw).rg;
95 return half4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
98 // TileMax filter (variable width)
99 half4 FragTileMaxV(VaryingsDefault i) : SV_Target
101 float2 uv0 = i.texcoord + _MainTex_TexelSize.xy * _TileMaxOffs.xy;
103 float2 du = float2(_MainTex_TexelSize.x, 0.0);
104 float2 dv = float2(0.0, _MainTex_TexelSize.y);
109 for (int ix = 0; ix < _TileMaxLoop; ix++)
112 for (int iy = 0; iy < _TileMaxLoop; iy++)
114 float2 uv = uv0 + du * ix + dv * iy;
115 vo = MaxV(vo, SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv).rg);
119 return half4(vo, 0.0, 0.0);
122 // NeighborMax filter
123 half4 FragNeighborMax(VaryingsDefault i) : SV_Target
125 const half cw = 1.01; // Center weight tweak
127 float4 d = _MainTex_TexelSize.xyxy * float4(1.0, 1.0, -1.0, 0.0);
129 half2 v1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord - d.xy).rg;
130 half2 v2 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord - d.wy).rg;
131 half2 v3 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord - d.zy).rg;
133 half2 v4 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord - d.xw).rg;
134 half2 v5 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord).rg * cw;
135 half2 v6 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.xw).rg;
137 half2 v7 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.zy).rg;
138 half2 v8 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.wy).rg;
139 half2 v9 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + d.xy).rg;
141 half2 va = MaxV(v1, MaxV(v2, v3));
142 half2 vb = MaxV(v4, MaxV(v5, v6));
143 half2 vc = MaxV(v7, MaxV(v8, v9));
145 return half4(MaxV(va, MaxV(vb, vc)) * (1.0 / cw), 0.0, 0.0);
148 // -----------------------------------------------------------------------------
151 // Returns true or false with a given interval.
152 bool Interval(half phase, half interval)
154 return frac(phase / interval) > 0.499;
157 // Jitter function for tile lookup
158 float2 JitterTile(float2 uv)
161 sincos(GradientNoise(uv + float2(2.0, 0.0)) * TWO_PI, ry, rx);
162 return float2(rx, ry) * _NeighborMaxTex_TexelSize.xy * 0.25;
165 // Velocity sampling function
166 half3 SampleVelocity(float2 uv)
168 half3 v = SAMPLE_TEXTURE2D_LOD(_VelocityTex, sampler_VelocityTex, uv, 0.0).xyz;
169 return half3((v.xy * 2.0 - 1.0) * _MaxBlurRadius, v.z);
172 // Reconstruction filter
173 half4 FragReconstruction(VaryingsDefault i) : SV_Target
175 // Color sample at the center point
176 const half4 c_p = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
178 // Velocity/Depth sample at the center point
179 const half3 vd_p = SampleVelocity(i.texcoord);
180 const half l_v_p = max(length(vd_p.xy), 0.5);
181 const half rcp_d_p = 1.0 / vd_p.z;
183 // NeighborMax vector sample at the center point
184 const half2 v_max = SAMPLE_TEXTURE2D(_NeighborMaxTex, sampler_NeighborMaxTex, i.texcoord + JitterTile(i.texcoord)).xy;
185 const half l_v_max = length(v_max);
186 const half rcp_l_v_max = 1.0 / l_v_max;
188 // Escape early if the NeighborMax vector is small enough.
189 if (l_v_max < 2.0) return c_p;
191 // Use V_p as a secondary sampling direction except when it's too small
192 // compared to V_max. This vector is rescaled to be the length of V_max.
193 const half2 v_alt = (l_v_p * 2.0 > l_v_max) ? vd_p.xy * (l_v_max / l_v_p) : v_max;
195 // Determine the sample count.
196 const half sc = floor(min(_LoopCount, l_v_max * 0.5));
198 // Loop variables (starts from the outermost sample)
199 const half dt = 1.0 / sc;
200 const half t_offs = (GradientNoise(i.texcoord) - 0.5) * dt;
201 half t = 1.0 - dt * 0.5;
204 // Background velocity
205 // This is used for tracking the maximum velocity in the background layer.
206 half l_v_bg = max(l_v_p, 1.0);
212 while (t > dt * 0.25)
214 // Sampling direction (switched per every two samples)
215 const half2 v_s = Interval(count, 4.0) ? v_alt : v_max;
217 // Sample position (inverted per every sample)
218 const half t_s = (Interval(count, 2.0) ? -t : t) + t_offs;
220 // Distance to the sample position
221 const half l_t = l_v_max * abs(t_s);
223 // UVs for the sample position
224 const float2 uv0 = i.texcoord + v_s * t_s * _MainTex_TexelSize.xy;
225 const float2 uv1 = i.texcoord + v_s * t_s * _VelocityTex_TexelSize.xy;
228 const half3 c = SAMPLE_TEXTURE2D_LOD(_MainTex, sampler_MainTex, uv0, 0.0).rgb;
230 // Velocity/Depth sample
231 const half3 vd = SampleVelocity(uv1);
233 // Background/Foreground separation
234 const half fg = saturate((vd_p.z - vd.z) * 20.0 * rcp_d_p);
236 // Length of the velocity vector
237 const half l_v = lerp(l_v_bg, length(vd.xy), fg);
240 // (Distance test) * (Spreading out by motion) * (Triangular window)
241 const half w = saturate(l_v - l_t) / l_v * (1.2 - t);
243 // Color accumulation
244 acc += half4(c, 1.0) * w;
246 // Update the background velocity.
247 l_v_bg = max(l_v_bg, l_v);
249 // Advance to the next sample.
250 t = Interval(count, 2.0) ? t - dt : t;
254 // Add the center sample.
255 acc += half4(c_p.rgb, 1.0) * (1.2 / (l_v_bg * sc * 2.0));
257 return half4(acc.rgb / acc.a, c_p.a);
264 Cull Off ZWrite Off ZTest Always
266 // (0) Velocity texture setup
271 #pragma vertex VertDefault
272 #pragma fragment FragVelocitySetup
277 // (1) TileMax filter (2 pixel width with normalization)
282 #pragma vertex VertDefault
283 #pragma fragment FragTileMax1
288 // (2) TileMax filter (2 pixel width)
293 #pragma vertex VertDefault
294 #pragma fragment FragTileMax2
299 // (3) TileMax filter (variable width)
304 #pragma vertex VertDefault
305 #pragma fragment FragTileMaxV
310 // (4) NeighborMax filter
315 #pragma vertex VertDefault
316 #pragma fragment FragNeighborMax
321 // (5) Reconstruction filter
326 #pragma vertex VertDefault
327 #pragma fragment FragReconstruction