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