DepthOfField.cs 18 KB

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