1 using System;
2 using UnityEngine;
3
4 namespace UnityStandardAssets.ImageEffects
5 {
6 [ExecuteInEditMode]
7 [RequireComponent (typeof(Camera))]
8 [AddComponentMenu ("Image Effects/Camera/Depth of Field (Lens Blur, Scatter, DX11)") ]
9 public class DepthOfField : PostEffectsBase {
10
11 public bool visualizeFocus = false;
12 public float focalLength = 10.0f;
13 public float focalSize = 0.05f;
14 public float aperture = 0.5f;
15 public Transform focalTransform = null;
16 public float maxBlurSize = 2.0f;
17 public bool highResolution = false;
18
19 public enum BlurType {
20 DiscBlur = 0,
21 DX11 = 1,
22 }
23
24 public enum BlurSampleCount {
25 Low = 0,
26 Medium = 1,
27 High = 2,
28 }
29
30 public BlurType blurType = BlurType.DiscBlur;
31 public BlurSampleCount blurSampleCount = BlurSampleCount.High;
32
33 public bool nearBlur = false;
34 public float foregroundOverlap = 1.0f;
35
36 public Shader dofHdrShader;
37 private Material dofHdrMaterial = null;
38
39 public Shader dx11BokehShader;
40 private Material dx11bokehMaterial;
41
42 public float dx11BokehThreshold = 0.5f;
43 public float dx11SpawnHeuristic = 0.0875f;
44 public Texture2D dx11BokehTexture = null;
45 public float dx11BokehScale = 1.2f;
46 public float dx11BokehIntensity = 2.5f;
47
48 private float focalDistance01 = 10.0f;
49 private ComputeBuffer cbDrawArgs;
50 private ComputeBuffer cbPoints;
51 private float internalBlurWidth = 1.0f;
52
53 private Camera cachedCamera;
54
55 public override bool CheckResources () {
56 CheckSupport (true); // only requires depth, not HDR
57
58 dofHdrMaterial = CheckShaderAndCreateMaterial (dofHdrShader, dofHdrMaterial);
59 if (supportDX11 && blurType == BlurType.DX11) {
60 dx11bokehMaterial = CheckShaderAndCreateMaterial(dx11BokehShader, dx11bokehMaterial);
61 CreateComputeResources ();
62 }
63
64 if (!isSupported)
65 ReportAutoDisable ();
66
67 return isSupported;
68 }
69
70 void OnEnable () {
71 cachedCamera = GetComponent<Camera>();
72 cachedCamera.depthTextureMode |= DepthTextureMode.Depth;
73 }
74
75 void OnDisable () {
76 ReleaseComputeResources ();
77
78 if (dofHdrMaterial) DestroyImmediate(dofHdrMaterial);
79 dofHdrMaterial = null;
80 if (dx11bokehMaterial) DestroyImmediate(dx11bokehMaterial);
81 dx11bokehMaterial = null;
82 }
83
84 void ReleaseComputeResources () {
85 if (cbDrawArgs != null) cbDrawArgs.Release();
86 cbDrawArgs = null;
87 if (cbPoints != null) cbPoints.Release();
88 cbPoints = null;
89 }
90
91 void CreateComputeResources () {
92 if (cbDrawArgs == null)
93 {
94 cbDrawArgs = new ComputeBuffer (1, 16, ComputeBufferType.IndirectArguments);
95 var args= new int[4];
96 args[0] = 0; args[1] = 1; args[2] = 0; args[3] = 0;
97 cbDrawArgs.SetData (args);
98 }
99 if (cbPoints == null)
100 {
101 cbPoints = new ComputeBuffer (90000, 12+16, ComputeBufferType.Append);
102 }
103 }
104
105 float FocalDistance01 ( float worldDist) {
106 return cachedCamera.WorldToViewportPoint((worldDist-cachedCamera.nearClipPlane) * cachedCamera.transform.forward + cachedCamera.transform.position).z / (cachedCamera.farClipPlane-cachedCamera.nearClipPlane);
107 }
108
109 private void WriteCoc ( RenderTexture fromTo, bool fgDilate) {
110 dofHdrMaterial.SetTexture("_FgOverlap", null);
111
112 if (nearBlur && fgDilate) {
113
114 int rtW = fromTo.width/2;
115 int rtH = fromTo.height/2;
116
117 // capture fg coc
118 RenderTexture temp2 = RenderTexture.GetTemporary (rtW, rtH, 0, fromTo.format);
119 Graphics.Blit (fromTo, temp2, dofHdrMaterial, 4);
120
121 // special blur
122 float fgAdjustment = internalBlurWidth * foregroundOverlap;
123
124 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, fgAdjustment , 0.0f, fgAdjustment));
125 RenderTexture temp1 = RenderTexture.GetTemporary (rtW, rtH, 0, fromTo.format);
126 Graphics.Blit (temp2, temp1, dofHdrMaterial, 2);
127 RenderTexture.ReleaseTemporary(temp2);
128
129 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (fgAdjustment, 0.0f, 0.0f, fgAdjustment));
130 temp2 = RenderTexture.GetTemporary (rtW, rtH, 0, fromTo.format);
131 Graphics.Blit (temp1, temp2, dofHdrMaterial, 2);
132 RenderTexture.ReleaseTemporary(temp1);
133
134 // "merge up" with background COC
135 dofHdrMaterial.SetTexture("_FgOverlap", temp2);
136 fromTo.MarkRestoreExpected(); // only touching alpha channel, RT restore expected
137 Graphics.Blit (fromTo, fromTo, dofHdrMaterial, 13);
138 RenderTexture.ReleaseTemporary(temp2);
139 }
140 else {
141 // capture full coc in alpha channel (fromTo is not read, but bound to detect screen flip)
142 fromTo.MarkRestoreExpected(); // only touching alpha channel, RT restore expected
143 Graphics.Blit (fromTo, fromTo, dofHdrMaterial, 0);
144 }
145 }
146
147 void OnRenderImage (RenderTexture source, RenderTexture destination) {
148 if (!CheckResources ()) {
149 Graphics.Blit (source, destination);
150 return;
151 }
152
153 // clamp & prepare values so they make sense
154
155 if (aperture < 0.0f) aperture = 0.0f;
156 if (maxBlurSize < 0.1f) maxBlurSize = 0.1f;
157 focalSize = Mathf.Clamp(focalSize, 0.0f, 2.0f);
158 internalBlurWidth = Mathf.Max(maxBlurSize, 0.0f);
159
160 // focal & coc calculations
161
162 focalDistance01 = (focalTransform) ? (cachedCamera.WorldToViewportPoint (focalTransform.position)).z / (cachedCamera.farClipPlane) : FocalDistance01 (focalLength);
163 dofHdrMaterial.SetVector("_CurveParams", new Vector4(1.0f, focalSize, (1.0f / (1.0f - aperture) - 1.0f), focalDistance01));
164
165 // possible render texture helpers
166
167 RenderTexture rtLow = null;
168 RenderTexture rtLow2 = null;
169 RenderTexture rtSuperLow1 = null;
170 RenderTexture rtSuperLow2 = null;
171 float fgBlurDist = internalBlurWidth * foregroundOverlap;
172
173 if (visualizeFocus)
174 {
175
176 //
177 // 2.
178 // visualize coc
179 //
180 //
181
182 WriteCoc (source, true);
183 Graphics.Blit (source, destination, dofHdrMaterial, 16);
184 }
185 else if ((blurType == BlurType.DX11) && dx11bokehMaterial)
186 {
187
188 //
189 // 1.
190 // optimized dx11 bokeh scatter
191 //
192 //
193
194
195 if (highResolution) {
196
197 internalBlurWidth = internalBlurWidth < 0.1f ? 0.1f : internalBlurWidth;
198 fgBlurDist = internalBlurWidth * foregroundOverlap;
199
200 rtLow = RenderTexture.GetTemporary (source.width, source.height, 0, source.format);
201
202 var dest2= RenderTexture.GetTemporary (source.width, source.height, 0, source.format);
203
204 // capture COC
205 WriteCoc (source, false);
206
207 // blur a bit so we can do a frequency check
208 rtSuperLow1 = RenderTexture.GetTemporary(source.width>>1, source.height>>1, 0, source.format);
209 rtSuperLow2 = RenderTexture.GetTemporary(source.width>>1, source.height>>1, 0, source.format);
210
211 Graphics.Blit(source, rtSuperLow1, dofHdrMaterial, 15);
212 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, 1.5f , 0.0f, 1.5f));
213 Graphics.Blit (rtSuperLow1, rtSuperLow2, dofHdrMaterial, 19);
214 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (1.5f, 0.0f, 0.0f, 1.5f));
215 Graphics.Blit (rtSuperLow2, rtSuperLow1, dofHdrMaterial, 19);
216
217 // capture fg coc
218 if (nearBlur)
219 Graphics.Blit (source, rtSuperLow2, dofHdrMaterial, 4);
220
221 dx11bokehMaterial.SetTexture ("_BlurredColor", rtSuperLow1);
222 dx11bokehMaterial.SetFloat ("_SpawnHeuristic", dx11SpawnHeuristic);
223 dx11bokehMaterial.SetVector ("_BokehParams", new Vector4(dx11BokehScale, dx11BokehIntensity, Mathf.Clamp(dx11BokehThreshold, 0.005f, 4.0f), internalBlurWidth));
224 dx11bokehMaterial.SetTexture ("_FgCocMask", nearBlur ? rtSuperLow2 : null);
225
226 // collect bokeh candidates and replace with a darker pixel
227 Graphics.SetRandomWriteTarget (1, cbPoints);
228 Graphics.Blit (source, rtLow, dx11bokehMaterial, 0);
229 Graphics.ClearRandomWriteTargets ();
230
231 // fg coc blur happens here (after collect!)
232 if (nearBlur) {
233 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, fgBlurDist , 0.0f, fgBlurDist));
234 Graphics.Blit (rtSuperLow2, rtSuperLow1, dofHdrMaterial, 2);
235 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (fgBlurDist, 0.0f, 0.0f, fgBlurDist));
236 Graphics.Blit (rtSuperLow1, rtSuperLow2, dofHdrMaterial, 2);
237
238 // merge fg coc with bg coc
239 Graphics.Blit (rtSuperLow2, rtLow, dofHdrMaterial, 3);
240 }
241
242 // NEW: LAY OUT ALPHA on destination target so we get nicer outlines for the high rez version
243 Graphics.Blit (rtLow, dest2, dofHdrMaterial, 20);
244
245 // box blur (easier to merge with bokeh buffer)
246 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (internalBlurWidth, 0.0f , 0.0f, internalBlurWidth));
247 Graphics.Blit (rtLow, source, dofHdrMaterial, 5);
248 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.0f, internalBlurWidth));
249 Graphics.Blit (source, dest2, dofHdrMaterial, 21);
250
251 // apply bokeh candidates
252 Graphics.SetRenderTarget (dest2);
253 ComputeBuffer.CopyCount (cbPoints, cbDrawArgs, 0);
254 dx11bokehMaterial.SetBuffer ("pointBuffer", cbPoints);
255 dx11bokehMaterial.SetTexture ("_MainTex", dx11BokehTexture);
256 dx11bokehMaterial.SetVector ("_Screen", new Vector3(1.0f/(1.0f*source.width), 1.0f/(1.0f*source.height), internalBlurWidth));
257 dx11bokehMaterial.SetPass (2);
258
259 Graphics.DrawProceduralIndirect (MeshTopology.Points, cbDrawArgs, 0);
260
261 Graphics.Blit (dest2, destination); // hackaround for DX11 high resolution flipfun (OPTIMIZEME)
262
263 RenderTexture.ReleaseTemporary(dest2);
264 RenderTexture.ReleaseTemporary(rtSuperLow1);
265 RenderTexture.ReleaseTemporary(rtSuperLow2);
266 }
267 else {
268 rtLow = RenderTexture.GetTemporary (source.width>>1, source.height>>1, 0, source.format);
269 rtLow2 = RenderTexture.GetTemporary (source.width>>1, source.height>>1, 0, source.format);
270
271 fgBlurDist = internalBlurWidth * foregroundOverlap;
272
273 // capture COC & color in low resolution
274 WriteCoc (source, false);
275 source.filterMode = FilterMode.Bilinear;
276 Graphics.Blit (source, rtLow, dofHdrMaterial, 6);
277
278 // blur a bit so we can do a frequency check
279 rtSuperLow1 = RenderTexture.GetTemporary(rtLow.width>>1, rtLow.height>>1, 0, rtLow.format);
280 rtSuperLow2 = RenderTexture.GetTemporary(rtLow.width>>1, rtLow.height>>1, 0, rtLow.format);
281
282 Graphics.Blit(rtLow, rtSuperLow1, dofHdrMaterial, 15);
283 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, 1.5f , 0.0f, 1.5f));
284 Graphics.Blit (rtSuperLow1, rtSuperLow2, dofHdrMaterial, 19);
285 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (1.5f, 0.0f, 0.0f, 1.5f));
286 Graphics.Blit (rtSuperLow2, rtSuperLow1, dofHdrMaterial, 19);
287
288 RenderTexture rtLow3 = null;
289
290 if (nearBlur) {
291 // capture fg coc
292 rtLow3 = RenderTexture.GetTemporary (source.width>>1, source.height>>1, 0, source.format);
293 Graphics.Blit (source, rtLow3, dofHdrMaterial, 4);
294 }
295
296 dx11bokehMaterial.SetTexture ("_BlurredColor", rtSuperLow1);
297 dx11bokehMaterial.SetFloat ("_SpawnHeuristic", dx11SpawnHeuristic);
298 dx11bokehMaterial.SetVector ("_BokehParams", new Vector4(dx11BokehScale, dx11BokehIntensity, Mathf.Clamp(dx11BokehThreshold, 0.005f, 4.0f), internalBlurWidth));
299 dx11bokehMaterial.SetTexture ("_FgCocMask", rtLow3);
300
301 // collect bokeh candidates and replace with a darker pixel
302 Graphics.SetRandomWriteTarget (1, cbPoints);
303 Graphics.Blit (rtLow, rtLow2, dx11bokehMaterial, 0);
304 Graphics.ClearRandomWriteTargets ();
305
306 RenderTexture.ReleaseTemporary(rtSuperLow1);
307 RenderTexture.ReleaseTemporary(rtSuperLow2);
308
309 // fg coc blur happens here (after collect!)
310 if (nearBlur) {
311 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, fgBlurDist , 0.0f, fgBlurDist));
312 Graphics.Blit (rtLow3, rtLow, dofHdrMaterial, 2);
313 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (fgBlurDist, 0.0f, 0.0f, fgBlurDist));
314 Graphics.Blit (rtLow, rtLow3, dofHdrMaterial, 2);
315
316 // merge fg coc with bg coc
317 Graphics.Blit (rtLow3, rtLow2, dofHdrMaterial, 3);
318 }
319
320 // box blur (easier to merge with bokeh buffer)
321 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (internalBlurWidth, 0.0f , 0.0f, internalBlurWidth));
322 Graphics.Blit (rtLow2, rtLow, dofHdrMaterial, 5);
323 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.0f, internalBlurWidth));
324 Graphics.Blit (rtLow, rtLow2, dofHdrMaterial, 5);
325
326 // apply bokeh candidates
327 Graphics.SetRenderTarget (rtLow2);
328 ComputeBuffer.CopyCount (cbPoints, cbDrawArgs, 0);
329 dx11bokehMaterial.SetBuffer ("pointBuffer", cbPoints);
330 dx11bokehMaterial.SetTexture ("_MainTex", dx11BokehTexture);
331 dx11bokehMaterial.SetVector ("_Screen", new Vector3(1.0f/(1.0f*rtLow2.width), 1.0f/(1.0f*rtLow2.height), internalBlurWidth));
332 dx11bokehMaterial.SetPass (1);
333 Graphics.DrawProceduralIndirect (MeshTopology.Points, cbDrawArgs, 0);
334
335 // upsample & combine
336 dofHdrMaterial.SetTexture ("_LowRez", rtLow2);
337 dofHdrMaterial.SetTexture ("_FgOverlap", rtLow3);
338 dofHdrMaterial.SetVector ("_Offsets", ((1.0f*source.width)/(1.0f*rtLow2.width)) * internalBlurWidth * Vector4.one);
339 Graphics.Blit (source, destination, dofHdrMaterial, 9);
340
341 if (rtLow3) RenderTexture.ReleaseTemporary(rtLow3);
342 }
343 }
344 else
345 {
346
347 //
348 // 2.
349 // poisson disc style blur in low resolution
350 //
351 //
352
353 source.filterMode = FilterMode.Bilinear;
354
355 if (highResolution) internalBlurWidth *= 2.0f;
356
357 WriteCoc (source, true);
358
359 rtLow = RenderTexture.GetTemporary (source.width >> 1, source.height >> 1, 0, source.format);
360 rtLow2 = RenderTexture.GetTemporary (source.width >> 1, source.height >> 1, 0, source.format);
361
362 int blurPass = (blurSampleCount == BlurSampleCount.High || blurSampleCount == BlurSampleCount.Medium) ? 17 : 11;
363
364 if (highResolution) {
365 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.025f, internalBlurWidth));
366 Graphics.Blit (source, destination, dofHdrMaterial, blurPass);
367 }
368 else {
369 dofHdrMaterial.SetVector ("_Offsets", new Vector4 (0.0f, internalBlurWidth, 0.1f, internalBlurWidth));
370
371 // blur
372 Graphics.Blit (source, rtLow, dofHdrMaterial, 6);
373 Graphics.Blit (rtLow, rtLow2, dofHdrMaterial, blurPass);
374
375 // cheaper blur in high resolution, upsample and combine
376 dofHdrMaterial.SetTexture("_LowRez", rtLow2);
377 dofHdrMaterial.SetTexture("_FgOverlap", null);
378 dofHdrMaterial.SetVector ("_Offsets", Vector4.one * ((1.0f*source.width)/(1.0f*rtLow2.width)) * internalBlurWidth);
379 Graphics.Blit (source, destination, dofHdrMaterial, blurSampleCount == BlurSampleCount.High ? 18 : 12);
380 }
381 }
382
383 if (rtLow) RenderTexture.ReleaseTemporary(rtLow);
384 if (rtLow2) RenderTexture.ReleaseTemporary(rtLow2);
385 }
386 }
387 }