DepthOfField.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. using System;
  2. namespace UnityEngine.Rendering.PostProcessing
  3. {
  4. /// <summary>
  5. /// Convolution kernel size for the Depth of Field effect.
  6. /// </summary>
  7. public enum KernelSize
  8. {
  9. /// <summary>
  10. /// Small filter.
  11. /// </summary>
  12. Small,
  13. /// <summary>
  14. /// Medium filter.
  15. /// </summary>
  16. Medium,
  17. /// <summary>
  18. /// Large filter.
  19. /// </summary>
  20. Large,
  21. /// <summary>
  22. /// Very large filter.
  23. /// </summary>
  24. VeryLarge
  25. }
  26. /// <summary>
  27. /// A volume parameter holding a <see cref="KernelSize"/> value.
  28. /// </summary>
  29. [Serializable]
  30. public sealed class KernelSizeParameter : ParameterOverride<KernelSize> {}
  31. /// <summary>
  32. /// This class holds settings for the Depth of Field effect.
  33. /// </summary>
  34. [Serializable]
  35. [PostProcess(typeof(DepthOfFieldRenderer), "Unity/Depth of Field", false)]
  36. public sealed class DepthOfField : PostProcessEffectSettings
  37. {
  38. /// <summary>
  39. /// The distance to the point of focus.
  40. /// </summary>
  41. [Min(0.1f), Tooltip("Distance to the point of focus.")]
  42. public FloatParameter focusDistance = new FloatParameter { value = 10f };
  43. /// <summary>
  44. /// The ratio of the aperture (known as f-stop or f-number). The smaller the value is, the
  45. /// shallower the depth of field is.
  46. /// </summary>
  47. [Range(0.05f, 32f), Tooltip("Ratio of aperture (known as f-stop or f-number). The smaller the value is, the shallower the depth of field is.")]
  48. public FloatParameter aperture = new FloatParameter { value = 5.6f };
  49. /// <summary>
  50. /// The distance between the lens and the film. The larger the value is, the shallower the
  51. /// depth of field is.
  52. /// </summary>
  53. [Range(1f, 300f), Tooltip("Distance between the lens and the film. The larger the value is, the shallower the depth of field is.")]
  54. public FloatParameter focalLength = new FloatParameter { value = 50f };
  55. /// <summary>
  56. /// The convolution kernel size of the bokeh filter, which determines the maximum radius of
  57. /// bokeh. It also affects the performance (the larger the kernel is, the longer the GPU
  58. /// time is required).
  59. /// </summary>
  60. [DisplayName("Max Blur Size"), Tooltip("Convolution kernel size of the bokeh filter, which determines the maximum radius of bokeh. It also affects performances (the larger the kernel is, the longer the GPU time is required).")]
  61. public KernelSizeParameter kernelSize = new KernelSizeParameter { value = KernelSize.Medium };
  62. /// <inheritdoc />
  63. public override bool IsEnabledAndSupported(PostProcessRenderContext context)
  64. {
  65. return enabled.value
  66. && SystemInfo.graphicsShaderLevel >= 35;
  67. }
  68. }
  69. // TODO: Doesn't play nice with alpha propagation, see if it can be fixed without killing performances
  70. internal sealed class DepthOfFieldRenderer : PostProcessEffectRenderer<DepthOfField>
  71. {
  72. enum Pass
  73. {
  74. CoCCalculation,
  75. CoCTemporalFilter,
  76. DownsampleAndPrefilter,
  77. BokehSmallKernel,
  78. BokehMediumKernel,
  79. BokehLargeKernel,
  80. BokehVeryLargeKernel,
  81. PostFilter,
  82. Combine,
  83. DebugOverlay
  84. }
  85. // Ping-pong between two history textures as we can't read & write the same target in the
  86. // same pass
  87. const int k_NumEyes = 2;
  88. const int k_NumCoCHistoryTextures = 2;
  89. readonly RenderTexture[][] m_CoCHistoryTextures = new RenderTexture[k_NumEyes][];
  90. int[] m_HistoryPingPong = new int[k_NumEyes];
  91. // Height of the 35mm full-frame format (36mm x 24mm)
  92. // TODO: Should be set by a physical camera
  93. const float k_FilmHeight = 0.024f;
  94. public DepthOfFieldRenderer()
  95. {
  96. for (int eye = 0; eye < k_NumEyes; eye++)
  97. {
  98. m_CoCHistoryTextures[eye] = new RenderTexture[k_NumCoCHistoryTextures];
  99. m_HistoryPingPong[eye] = 0;
  100. }
  101. }
  102. public override DepthTextureMode GetCameraFlags()
  103. {
  104. return DepthTextureMode.Depth;
  105. }
  106. RenderTextureFormat SelectFormat(RenderTextureFormat primary, RenderTextureFormat secondary)
  107. {
  108. if (primary.IsSupported())
  109. return primary;
  110. if (secondary.IsSupported())
  111. return secondary;
  112. return RenderTextureFormat.Default;
  113. }
  114. float CalculateMaxCoCRadius(int screenHeight)
  115. {
  116. // Estimate the allowable maximum radius of CoC from the kernel
  117. // size (the equation below was empirically derived).
  118. float radiusInPixels = (float)settings.kernelSize.value * 4f + 6f;
  119. // Applying a 5% limit to the CoC radius to keep the size of
  120. // TileMax/NeighborMax small enough.
  121. return Mathf.Min(0.05f, radiusInPixels / screenHeight);
  122. }
  123. RenderTexture CheckHistory(int eye, int id, PostProcessRenderContext context, RenderTextureFormat format)
  124. {
  125. var rt = m_CoCHistoryTextures[eye][id];
  126. if (m_ResetHistory || rt == null || !rt.IsCreated() || rt.width != context.width || rt.height != context.height)
  127. {
  128. RenderTexture.ReleaseTemporary(rt);
  129. // TODO: The CoCCalculation CoCTex uses RenderTextureReadWrite.Linear, why isn't this?
  130. rt = context.GetScreenSpaceTemporaryRT(0, format);
  131. rt.name = "CoC History, Eye: " + eye + ", ID: " + id;
  132. rt.filterMode = FilterMode.Bilinear;
  133. rt.Create();
  134. m_CoCHistoryTextures[eye][id] = rt;
  135. }
  136. return rt;
  137. }
  138. public override void Render(PostProcessRenderContext context)
  139. {
  140. var colorFormat = RenderTextureFormat.DefaultHDR;
  141. var cocFormat = SelectFormat(RenderTextureFormat.R8, RenderTextureFormat.RHalf);
  142. // Avoid using R8 on OSX with Metal. #896121, https://goo.gl/MgKqu6
  143. #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) && !UNITY_2017_1_OR_NEWER
  144. if (SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Metal)
  145. cocFormat = SelectFormat(RenderTextureFormat.RHalf, RenderTextureFormat.Default);
  146. #endif
  147. // Material setup
  148. float scaledFilmHeight = k_FilmHeight * (context.height / 1080f);
  149. var f = settings.focalLength.value / 1000f;
  150. var s1 = Mathf.Max(settings.focusDistance.value, f);
  151. var aspect = (float)context.screenWidth / (float)context.screenHeight;
  152. var coeff = f * f / (settings.aperture.value * (s1 - f) * scaledFilmHeight * 2f);
  153. var maxCoC = CalculateMaxCoCRadius(context.screenHeight);
  154. var sheet = context.propertySheets.Get(context.resources.shaders.depthOfField);
  155. sheet.properties.Clear();
  156. sheet.properties.SetFloat(ShaderIDs.Distance, s1);
  157. sheet.properties.SetFloat(ShaderIDs.LensCoeff, coeff);
  158. sheet.properties.SetFloat(ShaderIDs.MaxCoC, maxCoC);
  159. sheet.properties.SetFloat(ShaderIDs.RcpMaxCoC, 1f / maxCoC);
  160. sheet.properties.SetFloat(ShaderIDs.RcpAspect, 1f / aspect);
  161. var cmd = context.command;
  162. cmd.BeginSample("DepthOfField");
  163. // CoC calculation pass
  164. context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.CoCTex, 0, cocFormat, RenderTextureReadWrite.Linear);
  165. cmd.BlitFullscreenTriangle(BuiltinRenderTextureType.None, ShaderIDs.CoCTex, sheet, (int)Pass.CoCCalculation);
  166. // CoC temporal filter pass when TAA is enabled
  167. if (context.IsTemporalAntialiasingActive())
  168. {
  169. float motionBlending = context.temporalAntialiasing.motionBlending;
  170. float blend = m_ResetHistory ? 0f : motionBlending; // Handles first frame blending
  171. var jitter = context.temporalAntialiasing.jitter;
  172. sheet.properties.SetVector(ShaderIDs.TaaParams, new Vector3(jitter.x, jitter.y, blend));
  173. int pp = m_HistoryPingPong[context.xrActiveEye];
  174. var historyRead = CheckHistory(context.xrActiveEye, ++pp % 2, context, cocFormat);
  175. var historyWrite = CheckHistory(context.xrActiveEye, ++pp % 2, context, cocFormat);
  176. m_HistoryPingPong[context.xrActiveEye] = ++pp % 2;
  177. cmd.BlitFullscreenTriangle(historyRead, historyWrite, sheet, (int)Pass.CoCTemporalFilter);
  178. cmd.ReleaseTemporaryRT(ShaderIDs.CoCTex);
  179. cmd.SetGlobalTexture(ShaderIDs.CoCTex, historyWrite);
  180. }
  181. // Downsampling and prefiltering pass
  182. context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.DepthOfFieldTex, 0, colorFormat, RenderTextureReadWrite.Default, FilterMode.Bilinear, context.width / 2, context.height / 2);
  183. cmd.BlitFullscreenTriangle(context.source, ShaderIDs.DepthOfFieldTex, sheet, (int)Pass.DownsampleAndPrefilter);
  184. // Bokeh simulation pass
  185. context.GetScreenSpaceTemporaryRT(cmd, ShaderIDs.DepthOfFieldTemp, 0, colorFormat, RenderTextureReadWrite.Default, FilterMode.Bilinear, context.width / 2, context.height / 2);
  186. cmd.BlitFullscreenTriangle(ShaderIDs.DepthOfFieldTex, ShaderIDs.DepthOfFieldTemp, sheet, (int)Pass.BokehSmallKernel + (int)settings.kernelSize.value);
  187. // Postfilter pass
  188. cmd.BlitFullscreenTriangle(ShaderIDs.DepthOfFieldTemp, ShaderIDs.DepthOfFieldTex, sheet, (int)Pass.PostFilter);
  189. cmd.ReleaseTemporaryRT(ShaderIDs.DepthOfFieldTemp);
  190. // Debug overlay pass
  191. if (context.IsDebugOverlayEnabled(DebugOverlay.DepthOfField))
  192. context.PushDebugOverlay(cmd, context.source, sheet, (int)Pass.DebugOverlay);
  193. // Combine pass
  194. cmd.BlitFullscreenTriangle(context.source, context.destination, sheet, (int)Pass.Combine);
  195. cmd.ReleaseTemporaryRT(ShaderIDs.DepthOfFieldTex);
  196. if (!context.IsTemporalAntialiasingActive())
  197. cmd.ReleaseTemporaryRT(ShaderIDs.CoCTex);
  198. cmd.EndSample("DepthOfField");
  199. m_ResetHistory = false;
  200. }
  201. public override void Release()
  202. {
  203. for (int eye = 0; eye < k_NumEyes; eye++)
  204. {
  205. for (int i = 0; i < m_CoCHistoryTextures[eye].Length; i++)
  206. {
  207. RenderTexture.ReleaseTemporary(m_CoCHistoryTextures[eye][i]);
  208. m_CoCHistoryTextures[eye][i] = null;
  209. }
  210. m_HistoryPingPong[eye] = 0;
  211. }
  212. ResetHistory();
  213. }
  214. }
  215. }