Skip to content
This repository was archived by the owner on Nov 30, 2020. It is now read-only.

Commit 9ec5df8

Browse files
committed
New temporal filter for DoF
This commit includes major improvements to the temporal filter for DoF. See the following document for details on the improvements. https://goo.gl/3Rpxcz This commit also includes changes to the other components. **Changes to the TAA component** - Added `jitterVector` property to share the current jitter vector with DoF. **Changes to the Uber shader** - Changed to use a full-resolution CoC buffer to blend DoF results in the Uber pass. This is needed to eliminate seams and aliasing caused by integration with TAA. This also improves the quality even when TAA is disabled. **Changes to Common.cginc** - Added `SEPARATE_TEXTURE_SAMPLER` macro that controls how MainTex is to be declared (sampler2D or separate texture/sampler). The following is a brief list of the changes on the DoF component. - Implemented a temporal reprojection filter for the CoC history buffer. It's a simplified variant of the TAA filter and tailored to have nearly-identical characteristics with a less cost. - Changed to use R8 render textures for storing CoC values instead of RHalf. This is needed to compensate performance loss due to increase of the memory and bandwidth usage. - Optimized with using the texture gather functions on Shader Model 5. - Optimized the post blur filter. The sample count was reduced from 9 to 4 without losing quality.
1 parent e1f1d2c commit 9ec5df8

File tree

7 files changed

+323
-166
lines changed

7 files changed

+323
-166
lines changed

PostProcessing/Resources/Shaders/Common.cginc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
// -----------------------------------------------------------------------------
1717
// Uniforms
1818

19+
#if defined(SEPARATE_TEXTURE_SAMPLER)
20+
Texture2D _MainTex;
21+
SamplerState sampler_MainTex;
22+
#else
1923
sampler2D _MainTex;
24+
#endif
2025
float4 _MainTex_TexelSize;
2126
float4 _MainTex_ST;
2227

Lines changed: 117 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
#ifndef __DEPTH_OF_FIELD__
22
#define __DEPTH_OF_FIELD__
33

4-
#include "UnityCG.cginc"
4+
#if SHADER_TARGET >= 50
5+
// Use separate texture/sampler objects on Shader Model 5.0
6+
#define SEPARATE_TEXTURE_SAMPLER
7+
#define DOF_DECL_TEX2D(tex) Texture2D tex; SamplerState sampler##tex
8+
#define DOF_TEX2D(tex, coord) tex.Sample(sampler##tex, coord)
9+
#else
10+
#define DOF_DECL_TEX2D(tex) sampler2D tex
11+
#define DOF_TEX2D(tex, coord) tex2D(tex, coord)
12+
#endif
13+
514
#include "Common.cginc"
615
#include "DiskKernels.cginc"
716

8-
#define PREFILTER_LUMA_WEIGHT 1
9-
10-
sampler2D_float _CameraDepthTexture;
11-
sampler2D_float _HistoryCoC;
12-
float _HistoryWeight;
17+
DOF_DECL_TEX2D(_CameraDepthTexture);
18+
DOF_DECL_TEX2D(_CameraMotionVectorsTexture);
19+
DOF_DECL_TEX2D(_CoCTex);
1320

1421
// Camera parameters
1522
float _Distance;
1623
float _LensCoeff; // f^2 / (N * (S1 - f) * film_width * 2)
1724
float _MaxCoC;
1825
float _RcpMaxCoC;
1926
float _RcpAspect;
27+
half3 _TaaParams; // Jitter.x, Jitter.y, Blending
2028

2129
struct VaryingsDOF
2230
{
@@ -47,75 +55,114 @@ VaryingsDOF VertDOF(AttributesDefault v)
4755
return o;
4856
}
4957

50-
// Prefilter: CoC calculation, downsampling and premultiplying.
51-
52-
#if defined(PREFILTER_TAA)
58+
// CoC calculation
59+
half4 FragCoC(VaryingsDOF i) : SV_Target
60+
{
61+
float depth = LinearEyeDepth(DOF_TEX2D(_CameraDepthTexture, i.uv));
62+
half coc = (depth - _Distance) * _LensCoeff / max(depth, 1e-5);
63+
return saturate(coc * 0.5 * _RcpMaxCoC + 0.5);
64+
}
5365

54-
// TAA enabled: use MRT to update the history buffer in the same pass.
55-
struct PrefilterOutput
66+
// Temporal filter
67+
half4 FragTempFilter(VaryingsDOF i) : SV_Target
5668
{
57-
half4 base : SV_Target0;
58-
half4 history : SV_Target1;
59-
};
60-
#define PrefilterSemantics
69+
float3 uvOffs = _MainTex_TexelSize.xyy * float3(1, 1, 0);
70+
71+
#if defined(SEPARATE_TEXTURE_SAMPLER)
72+
73+
half4 cocTL = _CoCTex.GatherRed(sampler_CoCTex, i.uv - uvOffs.xy * 0.5); // top-left
74+
half4 cocBR = _CoCTex.GatherRed(sampler_CoCTex, i.uv + uvOffs.xy * 0.5); // bottom-right
75+
half coc1 = cocTL.x; // top
76+
half coc2 = cocTL.z; // left
77+
half coc3 = cocBR.x; // bottom
78+
half coc4 = cocBR.z; // right
6179

6280
#else
6381

64-
// No TAA
65-
#define PrefilterOutput half4
66-
#define PrefilterSemantics :SV_Target
82+
half coc1 = DOF_TEX2D(_CoCTex, i.uv - uvOffs.xz).r; // top
83+
half coc2 = DOF_TEX2D(_CoCTex, i.uv - uvOffs.zy).r; // left
84+
half coc3 = DOF_TEX2D(_CoCTex, i.uv + uvOffs.zy).r; // bottom
85+
half coc4 = DOF_TEX2D(_CoCTex, i.uv + uvOffs.xz).r; // right
6786

6887
#endif
6988

70-
PrefilterOutput FragPrefilter(VaryingsDOF i) PrefilterSemantics
89+
// Dejittered center sample.
90+
half coc0 = DOF_TEX2D(_CoCTex, i.uv - _TaaParams.xy).r;
91+
92+
// CoC dilation: determine the closest point in the four neighbors.
93+
float3 closest = float3(0, 0, coc0);
94+
closest = coc1 < closest.z ? float3(-uvOffs.xz, coc1) : closest;
95+
closest = coc2 < closest.z ? float3(-uvOffs.zy, coc2) : closest;
96+
closest = coc3 < closest.z ? float3(+uvOffs.zy, coc3) : closest;
97+
closest = coc4 < closest.z ? float3(+uvOffs.xz, coc4) : closest;
98+
99+
// Sample the history buffer with the motion vector at the closest point.
100+
float2 motion = DOF_TEX2D(_CameraMotionVectorsTexture, i.uv + closest.xy).xy;
101+
half cocHis = DOF_TEX2D(_MainTex, i.uv - motion).r;
102+
103+
// Neighborhood clamping.
104+
half cocMin = closest.z;
105+
half cocMax = max(max(max(max(coc0, coc1), coc2), coc3), coc4);
106+
cocHis = clamp(cocHis, cocMin, cocMax);
107+
108+
// Blend with the history.
109+
return lerp(coc0, cocHis, _TaaParams.z);
110+
}
111+
112+
// Prefilter: downsampling and premultiplying.
113+
half4 FragPrefilter(VaryingsDOF i) : SV_Target
71114
{
115+
#if defined(SEPARATE_TEXTURE_SAMPLER)
116+
117+
// Sample source colors.
118+
half4 c_r = _MainTex.GatherRed (sampler_MainTex, i.uv);
119+
half4 c_g = _MainTex.GatherGreen(sampler_MainTex, i.uv);
120+
half4 c_b = _MainTex.GatherBlue (sampler_MainTex, i.uv);
121+
122+
half3 c0 = half3(c_r.x, c_g.x, c_b.x);
123+
half3 c1 = half3(c_r.y, c_g.y, c_b.y);
124+
half3 c2 = half3(c_r.z, c_g.z, c_b.z);
125+
half3 c3 = half3(c_r.w, c_g.w, c_b.w);
126+
127+
// Sample CoCs.
128+
half4 cocs = _CoCTex.Gather(sampler_CoCTex, i.uvAlt) * 2.0 - 1.0;
129+
half coc0 = cocs.x;
130+
half coc1 = cocs.y;
131+
half coc2 = cocs.z;
132+
half coc3 = cocs.w;
133+
134+
#else
135+
72136
float3 duv = _MainTex_TexelSize.xyx * float3(0.5, 0.5, -0.5);
73137

74138
// Sample source colors.
75-
half3 c0 = tex2D(_MainTex, i.uv - duv.xy).rgb;
76-
half3 c1 = tex2D(_MainTex, i.uv - duv.zy).rgb;
77-
half3 c2 = tex2D(_MainTex, i.uv + duv.zy).rgb;
78-
half3 c3 = tex2D(_MainTex, i.uv + duv.xy).rgb;
79-
80-
// Sample linear depths.
81-
float d0 = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uvAlt - duv.xy));
82-
float d1 = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uvAlt - duv.zy));
83-
float d2 = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uvAlt + duv.zy));
84-
float d3 = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uvAlt + duv.xy));
85-
float4 depths = float4(d0, d1, d2, d3);
86-
87-
// Calculate the radiuses of CoCs at these sample points.
88-
float4 cocs = (depths - _Distance) * _LensCoeff / depths;
89-
cocs = clamp(cocs, -_MaxCoC, _MaxCoC);
90-
91-
#if defined(PREFILTER_TAA)
92-
// Get the average with the history to avoid temporal aliasing.
93-
half hcoc = tex2D(_HistoryCoC, i.uv).r;
94-
cocs = lerp(cocs, hcoc, _HistoryWeight);
95-
#endif
139+
half3 c0 = DOF_TEX2D(_MainTex, i.uv - duv.xy).rgb;
140+
half3 c1 = DOF_TEX2D(_MainTex, i.uv - duv.zy).rgb;
141+
half3 c2 = DOF_TEX2D(_MainTex, i.uv + duv.zy).rgb;
142+
half3 c3 = DOF_TEX2D(_MainTex, i.uv + duv.xy).rgb;
143+
144+
// Sample CoCs.
145+
half coc0 = DOF_TEX2D(_CoCTex, i.uvAlt - duv.xy).r * 2.0 - 1.0;
146+
half coc1 = DOF_TEX2D(_CoCTex, i.uvAlt - duv.zy).r * 2.0 - 1.0;
147+
half coc2 = DOF_TEX2D(_CoCTex, i.uvAlt + duv.zy).r * 2.0 - 1.0;
148+
half coc3 = DOF_TEX2D(_CoCTex, i.uvAlt + duv.xy).r * 2.0 - 1.0;
96149

97-
// Premultiply CoC to reduce background bleeding.
98-
float4 weights = saturate(abs(cocs) * _RcpMaxCoC);
99-
100-
#if defined(PREFILTER_LUMA_WEIGHT)
101-
// Apply luma weights to reduce flickering.
102-
// References:
103-
// http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
104-
// http://graphicrants.blogspot.fr/2013/12/tone-mapping.html
105-
weights.x *= 1.0 / (Max3(c0) + 1.0);
106-
weights.y *= 1.0 / (Max3(c1) + 1.0);
107-
weights.z *= 1.0 / (Max3(c2) + 1.0);
108-
weights.w *= 1.0 / (Max3(c3) + 1.0);
109150
#endif
110151

152+
// Apply CoC and luma weights to reduce bleeding and flickering.
153+
float w0 = abs(coc0) / (Max3(c0) + 1.0);
154+
float w1 = abs(coc1) / (Max3(c1) + 1.0);
155+
float w2 = abs(coc2) / (Max3(c2) + 1.0);
156+
float w3 = abs(coc3) / (Max3(c3) + 1.0);
157+
111158
// Weighted average of the color samples
112-
half3 avg = c0 * weights.x + c1 * weights.y + c2 * weights.z + c3 * weights.w;
113-
avg /= dot(weights, 1.0);
159+
half3 avg = c0 * w0 + c1 * w1 + c2 * w2 + c3 * w3;
160+
avg /= max(w0 + w1 + w2 + w3, 1e-5);
114161

115-
// Output CoC = average of CoCs
116-
half cocmin = Min4(cocs);
117-
half cocmax = Max4(cocs);
118-
half coc = -cocmin > cocmax ? cocmin : cocmax;
162+
// Select the largest CoC value.
163+
half coc_min = Min4(coc0, coc1, coc2, coc3);
164+
half coc_max = Max4(coc0, coc1, coc2, coc3);
165+
half coc = (-coc_min > coc_max ? coc_min : coc_max) * _MaxCoC;
119166

120167
// Premultiply CoC again.
121168
avg *= smoothstep(0, _MainTex_TexelSize.y * 2, abs(coc));
@@ -124,20 +171,13 @@ PrefilterOutput FragPrefilter(VaryingsDOF i) PrefilterSemantics
124171
avg = GammaToLinearSpace(avg);
125172
#endif
126173

127-
#if defined(PREFILTER_TAA)
128-
PrefilterOutput output;
129-
output.base = half4(avg, coc);
130-
output.history = coc.xxxx;
131-
return output;
132-
#else
133174
return half4(avg, coc);
134-
#endif
135175
}
136176

137177
// Bokeh filter with disk-shaped kernels
138178
half4 FragBlur(VaryingsDOF i) : SV_Target
139179
{
140-
half4 samp0 = tex2D(_MainTex, i.uv);
180+
half4 samp0 = DOF_TEX2D(_MainTex, i.uv);
141181

142182
half4 bgAcc = 0.0; // Background: far field bokeh
143183
half4 fgAcc = 0.0; // Foreground: near field bokeh
@@ -148,7 +188,7 @@ half4 FragBlur(VaryingsDOF i) : SV_Target
148188
float dist = length(disp);
149189

150190
float2 duv = float2(disp.x * _RcpAspect, disp.y);
151-
half4 samp = tex2D(_MainTex, i.uv + duv);
191+
half4 samp = DOF_TEX2D(_MainTex, i.uv + duv);
152192

153193
// BG: Compare CoC of the current sample and the center sample
154194
// and select smaller one.
@@ -181,50 +221,23 @@ half4 FragBlur(VaryingsDOF i) : SV_Target
181221
fgAcc.a *= UNITY_PI / kSampleCount;
182222

183223
// Alpha premultiplying
184-
half3 rgb = 0.0;
185-
rgb = lerp(rgb, bgAcc.rgb, saturate(bgAcc.a));
186-
rgb = lerp(rgb, fgAcc.rgb, saturate(fgAcc.a));
187-
188-
// Combined alpha value
189-
half alpha = (1.0 - saturate(bgAcc.a)) * (1.0 - saturate(fgAcc.a));
224+
half alpha = saturate(fgAcc.a);
225+
half3 rgb = lerp(bgAcc.rgb, fgAcc.rgb, alpha);
190226

191227
return half4(rgb, alpha);
192228
}
193229

194230
// Postfilter blur
195231
half4 FragPostBlur(VaryingsDOF i) : SV_Target
196232
{
197-
// 9-tap tent filter
198-
float4 duv = _MainTex_TexelSize.xyxy * float4(1, 1, -1, 0);
199-
200-
half4 c0 = tex2D(_MainTex, i.uv - duv.xy);
201-
half4 c1 = tex2D(_MainTex, i.uv - duv.wy);
202-
half4 c2 = tex2D(_MainTex, i.uv - duv.zy);
203-
204-
half4 c3 = tex2D(_MainTex, i.uv + duv.zw);
205-
half4 c4 = tex2D(_MainTex, i.uv );
206-
half4 c5 = tex2D(_MainTex, i.uv + duv.xw);
207-
208-
half4 c6 = tex2D(_MainTex, i.uv + duv.zy);
209-
half4 c7 = tex2D(_MainTex, i.uv + duv.wy);
210-
half4 c8 = tex2D(_MainTex, i.uv + duv.xy);
211-
212-
half4 acc = c0 * 1 + c1 * 2 + c2 * 1 +
213-
c3 * 2 + c4 * 4 + c5 * 2 +
214-
c6 * 1 + c7 * 2 + c8 * 1;
215-
216-
half aa =
217-
c0.a * c0.a * 1 + c1.a * c1.a * 2 + c2.a * c2.a * 1 +
218-
c3.a * c3.a * 2 + c4.a * c4.a * 4 + c5.a * c5.a * 2 +
219-
c6.a * c6.a * 1 + c7.a * c7.a * 2 + c8.a * c8.a * 1;
220-
221-
half wb = 1.2;
222-
half a = (wb * acc.a - aa) / (wb * 16 - acc.a);
223-
224-
acc /= 16;
225-
226-
half3 rgb = acc.rgb * (1 + saturate(acc.a - a));
227-
return half4(rgb, a);
233+
// 9 tap tent filter with 4 bilinear samples
234+
const float4 duv = _MainTex_TexelSize.xyxy * float4(0.5, 0.5, -0.5, 0);
235+
half4 acc;
236+
acc = DOF_TEX2D(_MainTex, i.uv - duv.xy);
237+
acc += DOF_TEX2D(_MainTex, i.uv - duv.zy);
238+
acc += DOF_TEX2D(_MainTex, i.uv + duv.zy);
239+
acc += DOF_TEX2D(_MainTex, i.uv + duv.xy);
240+
return acc / 4.0;
228241
}
229242

230243
#endif // __DEPTH_OF_FIELD__

0 commit comments

Comments
 (0)