BokehRenderPass.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using UnityEngine;
  4. using UnityEngine.Experimental.Rendering;
  5. using UnityEngine.InputSystem.Interactions;
  6. using UnityEngine.Rendering;
  7. using UnityEngine.Rendering.Universal;
  8. using UnityEngine.Rendering.Universal.Internal;
  9. namespace SicknessReduction.Visual.Rendering
  10. {
  11. internal static class ShaderConstants
  12. {
  13. public static readonly int _FullCoCTexture = Shader.PropertyToID("_FullCoCTexture");
  14. public static readonly int _DofTexture = Shader.PropertyToID("_DofTexture");
  15. public static readonly int _CoCParams = Shader.PropertyToID("_CoCParams");
  16. public static readonly int _BokehKernel = Shader.PropertyToID("_BokehKernel");
  17. public static readonly int _PongTexture = Shader.PropertyToID("_PongTexture");
  18. public static readonly int _PingTexture = Shader.PropertyToID("_PingTexture");
  19. }
  20. class BokehRenderPass : ScriptableRenderPass
  21. {
  22. // used to label this pass in Unity's Frame Debug utility
  23. string profilerTag;
  24. private Material material;
  25. private Material uberMaterial;
  26. private BokehFeature.BokehFeatureSettings bokehSettings;
  27. private Vector4[] m_BokehKernel;
  28. private bool kernelPrepared;
  29. RenderTextureDescriptor m_Descriptor;
  30. RenderTargetIdentifier cameraColorTargetIdent;
  31. RenderTargetHandle target;
  32. private RenderTargetHandle tmp;
  33. public BokehRenderPass(string profilerTag,
  34. BokehFeature.BokehFeatureSettings settings)
  35. {
  36. this.profilerTag = profilerTag;
  37. renderPassEvent = settings.WhenToInsert;
  38. bokehSettings = settings;
  39. material = CoreUtils.CreateEngineMaterial(
  40. Shader.Find("Hidden/Universal Render Pipeline/BokehDepthOfField"));
  41. Debug.Log("Material created: " + material?.shader?.name ?? "Null");
  42. }
  43. public void Setup(in RenderTextureDescriptor baseDescriptor, RenderTargetIdentifier target)
  44. {
  45. //m_Descriptor = baseDescriptor;
  46. cameraColorTargetIdent = target;
  47. }
  48. // called each frame before Execute, use it to set up things the pass will need
  49. public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
  50. {
  51. int wh = m_Descriptor.width / 2;
  52. int hh = m_Descriptor.height / 2;
  53. /*cmd.GetTemporaryRT(ShaderConstants._FullCoCTexture,
  54. cameraTextureDescriptor);
  55. cmd.GetTemporaryRT(ShaderConstants._PingTexture,
  56. cameraTextureDescriptor);
  57. cmd.GetTemporaryRT(ShaderConstants._PongTexture,
  58. cameraTextureDescriptor);
  59. cmd.GetTemporaryRT(target.id, cameraTextureDescriptor);*/
  60. m_Descriptor = cameraTextureDescriptor;
  61. // Temporary textures
  62. cmd.GetTemporaryRT(ShaderConstants._FullCoCTexture,
  63. GetStereoCompatibleDescriptor(m_Descriptor.width, m_Descriptor.height, GraphicsFormat.R8_UNorm),
  64. FilterMode.Bilinear);
  65. cmd.GetTemporaryRT(ShaderConstants._PingTexture,
  66. GetStereoCompatibleDescriptor(wh, hh, GraphicsFormat.R16G16B16A16_SFloat), FilterMode.Bilinear);
  67. cmd.GetTemporaryRT(ShaderConstants._PongTexture,
  68. GetStereoCompatibleDescriptor(wh, hh, GraphicsFormat.R16G16B16A16_SFloat), FilterMode.Bilinear);
  69. cmd.GetTemporaryRT(target.id, cameraTextureDescriptor);
  70. cmd.GetTemporaryRT(tmp.id, cameraTextureDescriptor);
  71. }
  72. // Execute is called for every eligible camera every frame. It's not called at the moment that
  73. // rendering is actually taking place, so don't directly execute rendering commands here.
  74. // Instead use the methods on ScriptableRenderContext to set up instructions.
  75. // RenderingData provides a bunch of (not very well documented) information about the scene
  76. // and what's being rendered.
  77. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
  78. {
  79. // fetch a command buffer to use
  80. CommandBuffer cmd = CommandBufferPool.Get(profilerTag);
  81. cmd.Clear();
  82. int wh = m_Descriptor.width / 2;
  83. int hh = m_Descriptor.height / 2;
  84. // "A Lens and Aperture Camera Model for Synthetic Image Generation" [Potmesil81]
  85. float maxCoC = bokehSettings.maxCoc;
  86. float P = bokehSettings.focusDistance;
  87. float maxRadius = GetMaxBokehRadiusInPixels(m_Descriptor.height);
  88. float rcpAspect = 1f / (wh / (float) hh);
  89. cmd.SetGlobalVector(ShaderConstants._CoCParams,
  90. new Vector4(P, maxCoC, maxRadius, rcpAspect));
  91. // Prepare the bokeh kernel constant buffer
  92. if (!kernelPrepared)
  93. {
  94. PrepareBokehKernel();
  95. kernelPrepared = true;
  96. }
  97. cmd.SetGlobalVectorArray(ShaderConstants._BokehKernel, m_BokehKernel);
  98. // Compute CoC
  99. cmd.Blit(cameraColorTargetIdent, ShaderConstants._FullCoCTexture, material, 0);
  100. cmd.SetGlobalTexture(ShaderConstants._FullCoCTexture,
  101. ShaderConstants._FullCoCTexture);
  102. // Downscale & prefilter color + coc
  103. cmd.Blit(cameraColorTargetIdent, ShaderConstants._PingTexture, material, 1);
  104. // Bokeh blur
  105. cmd.Blit(ShaderConstants._PingTexture, ShaderConstants._PongTexture,
  106. material, 2);
  107. // Post-filtering
  108. cmd.Blit(ShaderConstants._PongTexture,
  109. BlitDstDiscardContent(cmd, ShaderConstants._PingTexture), material, 3);
  110. // Composite
  111. cmd.SetGlobalTexture(ShaderConstants._DofTexture,
  112. ShaderConstants._PingTexture);
  113. cmd.Blit(cameraColorTargetIdent, BlitDstDiscardContent(cmd, target.Identifier()), material, 4);
  114. //cmd.Blit(tmp.Identifier(), target.Identifier(), material, 5);
  115. //cmd.SetGlobalTexture("_BlitTex", cameraColorTargetIdent);
  116. //cmd.Blit(target.Identifier(), cameraColorTargetIdent, uberMaterial);
  117. cmd.Blit(target.Identifier(), cameraColorTargetIdent);
  118. context.ExecuteCommandBuffer(cmd);
  119. // tidy up after ourselves
  120. cmd.Clear();
  121. CommandBufferPool.Release(cmd);
  122. }
  123. // called after Execute, use it to clean up anything allocated in Configure
  124. public override void FrameCleanup(CommandBuffer cmd)
  125. {
  126. // Cleanup
  127. cmd.ReleaseTemporaryRT(ShaderConstants._FullCoCTexture);
  128. cmd.ReleaseTemporaryRT(ShaderConstants._PingTexture);
  129. cmd.ReleaseTemporaryRT(ShaderConstants._PongTexture);
  130. cmd.ReleaseTemporaryRT(target.id);
  131. cmd.ReleaseTemporaryRT(tmp.id);
  132. }
  133. #region Depth Of Field
  134. void PrepareBokehKernel()
  135. {
  136. const int kRings = 4;
  137. const int kPointsPerRing = 7;
  138. // Check the existing array
  139. if (m_BokehKernel == null)
  140. m_BokehKernel = new Vector4[42];
  141. // Fill in sample points (concentric circles transformed to rotated N-Gon)
  142. int idx = 0;
  143. float bladeCount = 5f;
  144. float curvature = 0f;
  145. float rotation = 10f * Mathf.Deg2Rad;
  146. const float PI = Mathf.PI;
  147. const float TWO_PI = Mathf.PI * 2f;
  148. for (int ring = 1; ring < kRings; ring++)
  149. {
  150. float bias = 1f / kPointsPerRing;
  151. float radius = (ring + bias) / (kRings - 1f + bias);
  152. int points = ring * kPointsPerRing;
  153. for (int point = 0; point < points; point++)
  154. {
  155. // Angle on ring
  156. float phi = 2f * PI * point / points;
  157. // Transform to rotated N-Gon
  158. // Adapted from "CryEngine 3 Graphics Gems" [Sousa13]
  159. float nt = Mathf.Cos(PI / bladeCount);
  160. float dt = Mathf.Cos(phi - (TWO_PI / bladeCount) *
  161. Mathf.Floor((bladeCount * phi + Mathf.PI) / TWO_PI));
  162. float r = radius * Mathf.Pow(nt / dt, curvature);
  163. float u = r * Mathf.Cos(phi - rotation);
  164. float v = r * Mathf.Sin(phi - rotation);
  165. m_BokehKernel[idx] = new Vector4(u, v);
  166. idx++;
  167. }
  168. }
  169. }
  170. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  171. static float GetMaxBokehRadiusInPixels(float viewportHeight)
  172. {
  173. // Estimate the maximum radius of bokeh (empirically derived from the ring count)
  174. const float kRadiusInPixels = 14f;
  175. return Mathf.Min(0.05f, kRadiusInPixels / viewportHeight);
  176. }
  177. RenderTextureDescriptor GetStereoCompatibleDescriptor()
  178. => GetStereoCompatibleDescriptor(m_Descriptor.width, m_Descriptor.height, m_Descriptor.graphicsFormat,
  179. m_Descriptor.depthBufferBits);
  180. RenderTextureDescriptor GetStereoCompatibleDescriptor(int width, int height, GraphicsFormat format,
  181. int depthBufferBits = 0)
  182. {
  183. // Inherit the VR setup from the camera descriptor
  184. var desc = m_Descriptor;
  185. desc.depthBufferBits = depthBufferBits;
  186. desc.msaaSamples = 1;
  187. desc.width = width;
  188. desc.height = height;
  189. desc.graphicsFormat = format;
  190. return desc;
  191. }
  192. private BuiltinRenderTextureType BlitDstDiscardContent(CommandBuffer cmd, RenderTargetIdentifier rt)
  193. {
  194. // We set depth to DontCare because rt might be the source of PostProcessing used as a temporary target
  195. // Source typically comes with a depth buffer and right now we don't have a way to only bind the color attachment of a RenderTargetIdentifier
  196. cmd.SetRenderTarget(new RenderTargetIdentifier(rt, 0, CubemapFace.Unknown, -1),
  197. RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store,
  198. RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare);
  199. return BuiltinRenderTextureType.CurrentActive;
  200. }
  201. #endregion
  202. }
  203. }