ScreenSpaceAmbientOcclusion.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. using System;
  2. namespace UnityEngine.Rendering.Universal
  3. {
  4. [Serializable]
  5. internal class ScreenSpaceAmbientOcclusionSettings
  6. {
  7. // Parameters
  8. [SerializeField] internal bool Downsample = false;
  9. [SerializeField] internal bool AfterOpaque = false;
  10. [SerializeField] internal DepthSource Source = DepthSource.DepthNormals;
  11. [SerializeField] internal NormalQuality NormalSamples = NormalQuality.Medium;
  12. [SerializeField] internal float Intensity = 3.0f;
  13. [SerializeField] internal float DirectLightingStrength = 0.25f;
  14. [SerializeField] internal float Radius = 0.035f;
  15. [SerializeField] internal int SampleCount = 4;
  16. // Enums
  17. internal enum DepthSource
  18. {
  19. Depth = 0,
  20. DepthNormals = 1
  21. }
  22. internal enum NormalQuality
  23. {
  24. Low,
  25. Medium,
  26. High
  27. }
  28. }
  29. [DisallowMultipleRendererFeature]
  30. [Tooltip("The Ambient Occlusion effect darkens creases, holes, intersections and surfaces that are close to each other.")]
  31. internal class ScreenSpaceAmbientOcclusion : ScriptableRendererFeature
  32. {
  33. // Serialized Fields
  34. [SerializeField, HideInInspector] private Shader m_Shader = null;
  35. [SerializeField] private ScreenSpaceAmbientOcclusionSettings m_Settings = new ScreenSpaceAmbientOcclusionSettings();
  36. // Private Fields
  37. private Material m_Material;
  38. private ScreenSpaceAmbientOcclusionPass m_SSAOPass = null;
  39. // Constants
  40. private const string k_ShaderName = "Hidden/Universal Render Pipeline/ScreenSpaceAmbientOcclusion";
  41. private const string k_OrthographicCameraKeyword = "_ORTHOGRAPHIC";
  42. private const string k_NormalReconstructionLowKeyword = "_RECONSTRUCT_NORMAL_LOW";
  43. private const string k_NormalReconstructionMediumKeyword = "_RECONSTRUCT_NORMAL_MEDIUM";
  44. private const string k_NormalReconstructionHighKeyword = "_RECONSTRUCT_NORMAL_HIGH";
  45. private const string k_SourceDepthKeyword = "_SOURCE_DEPTH";
  46. private const string k_SourceDepthNormalsKeyword = "_SOURCE_DEPTH_NORMALS";
  47. internal bool afterOpaque => m_Settings.AfterOpaque;
  48. /// <inheritdoc/>
  49. public override void Create()
  50. {
  51. // Create the pass...
  52. if (m_SSAOPass == null)
  53. {
  54. m_SSAOPass = new ScreenSpaceAmbientOcclusionPass();
  55. }
  56. GetMaterial();
  57. }
  58. /// <inheritdoc/>
  59. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
  60. {
  61. if (!GetMaterial())
  62. {
  63. Debug.LogErrorFormat(
  64. "{0}.AddRenderPasses(): Missing material. {1} render pass will not be added. Check for missing reference in the renderer resources.",
  65. GetType().Name, name);
  66. return;
  67. }
  68. bool shouldAdd = m_SSAOPass.Setup(m_Settings, renderer, m_Material);
  69. if (shouldAdd)
  70. {
  71. renderer.EnqueuePass(m_SSAOPass);
  72. }
  73. }
  74. /// <inheritdoc/>
  75. protected override void Dispose(bool disposing)
  76. {
  77. CoreUtils.Destroy(m_Material);
  78. }
  79. private bool GetMaterial()
  80. {
  81. if (m_Material != null)
  82. {
  83. return true;
  84. }
  85. if (m_Shader == null)
  86. {
  87. m_Shader = Shader.Find(k_ShaderName);
  88. if (m_Shader == null)
  89. {
  90. return false;
  91. }
  92. }
  93. m_Material = CoreUtils.CreateEngineMaterial(m_Shader);
  94. return m_Material != null;
  95. }
  96. // The SSAO Pass
  97. private class ScreenSpaceAmbientOcclusionPass : ScriptableRenderPass
  98. {
  99. // Properties
  100. private bool isRendererDeferred => m_Renderer != null && m_Renderer is UniversalRenderer && ((UniversalRenderer)m_Renderer).renderingMode == RenderingMode.Deferred;
  101. // Private Variables
  102. private bool m_SupportsR8RenderTextureFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8);
  103. private Material m_Material;
  104. private Vector4[] m_CameraTopLeftCorner = new Vector4[2];
  105. private Vector4[] m_CameraXExtent = new Vector4[2];
  106. private Vector4[] m_CameraYExtent = new Vector4[2];
  107. private Vector4[] m_CameraZExtent = new Vector4[2];
  108. private Matrix4x4[] m_CameraViewProjections = new Matrix4x4[2];
  109. private ProfilingSampler m_ProfilingSampler = ProfilingSampler.Get(URPProfileId.SSAO);
  110. private ScriptableRenderer m_Renderer = null;
  111. private RenderTargetIdentifier m_SSAOTexture1Target = new RenderTargetIdentifier(s_SSAOTexture1ID, 0, CubemapFace.Unknown, -1);
  112. private RenderTargetIdentifier m_SSAOTexture2Target = new RenderTargetIdentifier(s_SSAOTexture2ID, 0, CubemapFace.Unknown, -1);
  113. private RenderTargetIdentifier m_SSAOTexture3Target = new RenderTargetIdentifier(s_SSAOTexture3ID, 0, CubemapFace.Unknown, -1);
  114. private RenderTargetIdentifier m_SSAOTextureFinalTarget = new RenderTargetIdentifier(s_SSAOTextureFinalID, 0, CubemapFace.Unknown, -1);
  115. private RenderTextureDescriptor m_AOPassDescriptor;
  116. private RenderTextureDescriptor m_BlurPassesDescriptor;
  117. private RenderTextureDescriptor m_FinalDescriptor;
  118. private ScreenSpaceAmbientOcclusionSettings m_CurrentSettings;
  119. // Constants
  120. private const string k_SSAOTextureName = "_ScreenSpaceOcclusionTexture";
  121. private const string k_SSAOAmbientOcclusionParamName = "_AmbientOcclusionParam";
  122. // Statics
  123. private static readonly int s_BaseMapID = Shader.PropertyToID("_BaseMap");
  124. private static readonly int s_SSAOParamsID = Shader.PropertyToID("_SSAOParams");
  125. private static readonly int s_SSAOTexture1ID = Shader.PropertyToID("_SSAO_OcclusionTexture1");
  126. private static readonly int s_SSAOTexture2ID = Shader.PropertyToID("_SSAO_OcclusionTexture2");
  127. private static readonly int s_SSAOTexture3ID = Shader.PropertyToID("_SSAO_OcclusionTexture3");
  128. private static readonly int s_SSAOTextureFinalID = Shader.PropertyToID("_SSAO_OcclusionTexture");
  129. private static readonly int s_CameraViewXExtentID = Shader.PropertyToID("_CameraViewXExtent");
  130. private static readonly int s_CameraViewYExtentID = Shader.PropertyToID("_CameraViewYExtent");
  131. private static readonly int s_CameraViewZExtentID = Shader.PropertyToID("_CameraViewZExtent");
  132. private static readonly int s_ProjectionParams2ID = Shader.PropertyToID("_ProjectionParams2");
  133. private static readonly int s_CameraViewProjectionsID = Shader.PropertyToID("_CameraViewProjections");
  134. private static readonly int s_CameraViewTopLeftCornerID = Shader.PropertyToID("_CameraViewTopLeftCorner");
  135. private enum ShaderPasses
  136. {
  137. AO = 0,
  138. BlurHorizontal = 1,
  139. BlurVertical = 2,
  140. BlurFinal = 3,
  141. AfterOpaque = 4
  142. }
  143. internal ScreenSpaceAmbientOcclusionPass()
  144. {
  145. m_CurrentSettings = new ScreenSpaceAmbientOcclusionSettings();
  146. }
  147. internal bool Setup(ScreenSpaceAmbientOcclusionSettings featureSettings, ScriptableRenderer renderer, Material material)
  148. {
  149. m_Material = material;
  150. m_Renderer = renderer;
  151. m_CurrentSettings = featureSettings;
  152. ScreenSpaceAmbientOcclusionSettings.DepthSource source;
  153. if (isRendererDeferred)
  154. {
  155. renderPassEvent = featureSettings.AfterOpaque ? RenderPassEvent.AfterRenderingOpaques : RenderPassEvent.AfterRenderingGbuffer;
  156. source = ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
  157. }
  158. else
  159. {
  160. // Rendering after PrePasses is usually correct except when depth priming is in play:
  161. // then we rely on a depth resolve taking place after the PrePasses in order to have it ready for SSAO.
  162. // Hence we set the event to RenderPassEvent.AfterRenderingPrePasses + 1 at the earliest.
  163. renderPassEvent = featureSettings.AfterOpaque ? RenderPassEvent.AfterRenderingOpaques : RenderPassEvent.AfterRenderingPrePasses + 1;
  164. source = m_CurrentSettings.Source;
  165. }
  166. switch (source)
  167. {
  168. case ScreenSpaceAmbientOcclusionSettings.DepthSource.Depth:
  169. ConfigureInput(ScriptableRenderPassInput.Depth);
  170. break;
  171. case ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals:
  172. ConfigureInput(ScriptableRenderPassInput.Normal);// need depthNormal prepass for forward-only geometry
  173. break;
  174. default:
  175. throw new ArgumentOutOfRangeException();
  176. }
  177. return m_Material != null
  178. && m_CurrentSettings.Intensity > 0.0f
  179. && m_CurrentSettings.Radius > 0.0f
  180. && m_CurrentSettings.SampleCount > 0;
  181. }
  182. /// <inheritdoc/>
  183. public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
  184. {
  185. RenderTextureDescriptor cameraTargetDescriptor = renderingData.cameraData.cameraTargetDescriptor;
  186. int downsampleDivider = m_CurrentSettings.Downsample ? 2 : 1;
  187. // Update SSAO parameters in the material
  188. Vector4 ssaoParams = new Vector4(
  189. m_CurrentSettings.Intensity, // Intensity
  190. m_CurrentSettings.Radius, // Radius
  191. 1.0f / downsampleDivider, // Downsampling
  192. m_CurrentSettings.SampleCount // Sample count
  193. );
  194. m_Material.SetVector(s_SSAOParamsID, ssaoParams);
  195. #if ENABLE_VR && ENABLE_XR_MODULE
  196. int eyeCount = renderingData.cameraData.xr.enabled && renderingData.cameraData.xr.singlePassEnabled ? 2 : 1;
  197. #else
  198. int eyeCount = 1;
  199. #endif
  200. for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++)
  201. {
  202. Matrix4x4 view = renderingData.cameraData.GetViewMatrix(eyeIndex);
  203. Matrix4x4 proj = renderingData.cameraData.GetProjectionMatrix(eyeIndex);
  204. m_CameraViewProjections[eyeIndex] = proj * view;
  205. // camera view space without translation, used by SSAO.hlsl ReconstructViewPos() to calculate view vector.
  206. Matrix4x4 cview = view;
  207. cview.SetColumn(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
  208. Matrix4x4 cviewProj = proj * cview;
  209. Matrix4x4 cviewProjInv = cviewProj.inverse;
  210. Vector4 topLeftCorner = cviewProjInv.MultiplyPoint(new Vector4(-1, 1, -1, 1));
  211. Vector4 topRightCorner = cviewProjInv.MultiplyPoint(new Vector4(1, 1, -1, 1));
  212. Vector4 bottomLeftCorner = cviewProjInv.MultiplyPoint(new Vector4(-1, -1, -1, 1));
  213. Vector4 farCentre = cviewProjInv.MultiplyPoint(new Vector4(0, 0, 1, 1));
  214. m_CameraTopLeftCorner[eyeIndex] = topLeftCorner;
  215. m_CameraXExtent[eyeIndex] = topRightCorner - topLeftCorner;
  216. m_CameraYExtent[eyeIndex] = bottomLeftCorner - topLeftCorner;
  217. m_CameraZExtent[eyeIndex] = farCentre;
  218. }
  219. m_Material.SetVector(s_ProjectionParams2ID, new Vector4(1.0f / renderingData.cameraData.camera.nearClipPlane, 0.0f, 0.0f, 0.0f));
  220. m_Material.SetMatrixArray(s_CameraViewProjectionsID, m_CameraViewProjections);
  221. m_Material.SetVectorArray(s_CameraViewTopLeftCornerID, m_CameraTopLeftCorner);
  222. m_Material.SetVectorArray(s_CameraViewXExtentID, m_CameraXExtent);
  223. m_Material.SetVectorArray(s_CameraViewYExtentID, m_CameraYExtent);
  224. m_Material.SetVectorArray(s_CameraViewZExtentID, m_CameraZExtent);
  225. // Update keywords
  226. CoreUtils.SetKeyword(m_Material, k_OrthographicCameraKeyword, renderingData.cameraData.camera.orthographic);
  227. ScreenSpaceAmbientOcclusionSettings.DepthSource source = this.isRendererDeferred
  228. ? ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals
  229. : m_CurrentSettings.Source;
  230. if (source == ScreenSpaceAmbientOcclusionSettings.DepthSource.Depth)
  231. {
  232. switch (m_CurrentSettings.NormalSamples)
  233. {
  234. case ScreenSpaceAmbientOcclusionSettings.NormalQuality.Low:
  235. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionLowKeyword, true);
  236. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionMediumKeyword, false);
  237. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionHighKeyword, false);
  238. break;
  239. case ScreenSpaceAmbientOcclusionSettings.NormalQuality.Medium:
  240. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionLowKeyword, false);
  241. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionMediumKeyword, true);
  242. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionHighKeyword, false);
  243. break;
  244. case ScreenSpaceAmbientOcclusionSettings.NormalQuality.High:
  245. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionLowKeyword, false);
  246. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionMediumKeyword, false);
  247. CoreUtils.SetKeyword(m_Material, k_NormalReconstructionHighKeyword, true);
  248. break;
  249. default:
  250. throw new ArgumentOutOfRangeException();
  251. }
  252. }
  253. switch (source)
  254. {
  255. case ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals:
  256. CoreUtils.SetKeyword(m_Material, k_SourceDepthKeyword, false);
  257. CoreUtils.SetKeyword(m_Material, k_SourceDepthNormalsKeyword, true);
  258. break;
  259. default:
  260. CoreUtils.SetKeyword(m_Material, k_SourceDepthKeyword, true);
  261. CoreUtils.SetKeyword(m_Material, k_SourceDepthNormalsKeyword, false);
  262. break;
  263. }
  264. // Set up the descriptors
  265. RenderTextureDescriptor descriptor = cameraTargetDescriptor;
  266. descriptor.msaaSamples = 1;
  267. descriptor.depthBufferBits = 0;
  268. m_AOPassDescriptor = descriptor;
  269. m_AOPassDescriptor.width /= downsampleDivider;
  270. m_AOPassDescriptor.height /= downsampleDivider;
  271. m_AOPassDescriptor.colorFormat = RenderTextureFormat.ARGB32;
  272. m_BlurPassesDescriptor = descriptor;
  273. m_BlurPassesDescriptor.colorFormat = RenderTextureFormat.ARGB32;
  274. m_FinalDescriptor = descriptor;
  275. m_FinalDescriptor.colorFormat = m_SupportsR8RenderTextureFormat ? RenderTextureFormat.R8 : RenderTextureFormat.ARGB32;
  276. // Get temporary render textures
  277. cmd.GetTemporaryRT(s_SSAOTexture1ID, m_AOPassDescriptor, FilterMode.Bilinear);
  278. cmd.GetTemporaryRT(s_SSAOTexture2ID, m_BlurPassesDescriptor, FilterMode.Bilinear);
  279. cmd.GetTemporaryRT(s_SSAOTexture3ID, m_BlurPassesDescriptor, FilterMode.Bilinear);
  280. cmd.GetTemporaryRT(s_SSAOTextureFinalID, m_FinalDescriptor, FilterMode.Bilinear);
  281. // Configure targets and clear color
  282. ConfigureTarget(m_CurrentSettings.AfterOpaque ? m_Renderer.cameraColorTarget : s_SSAOTexture2ID);
  283. ConfigureClear(ClearFlag.None, Color.white);
  284. }
  285. /// <inheritdoc/>
  286. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
  287. {
  288. if (m_Material == null)
  289. {
  290. Debug.LogErrorFormat("{0}.Execute(): Missing material. ScreenSpaceAmbientOcclusion pass will not execute. Check for missing reference in the renderer resources.", GetType().Name);
  291. return;
  292. }
  293. CommandBuffer cmd = CommandBufferPool.Get();
  294. using (new ProfilingScope(cmd, m_ProfilingSampler))
  295. {
  296. if (!m_CurrentSettings.AfterOpaque)
  297. {
  298. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ScreenSpaceOcclusion, true);
  299. }
  300. PostProcessUtils.SetSourceSize(cmd, m_AOPassDescriptor);
  301. // Execute the SSAO
  302. Render(cmd, m_SSAOTexture1Target, ShaderPasses.AO);
  303. // Execute the Blur Passes
  304. RenderAndSetBaseMap(cmd, m_SSAOTexture1Target, m_SSAOTexture2Target, ShaderPasses.BlurHorizontal);
  305. PostProcessUtils.SetSourceSize(cmd, m_BlurPassesDescriptor);
  306. RenderAndSetBaseMap(cmd, m_SSAOTexture2Target, m_SSAOTexture3Target, ShaderPasses.BlurVertical);
  307. RenderAndSetBaseMap(cmd, m_SSAOTexture3Target, m_SSAOTextureFinalTarget, ShaderPasses.BlurFinal);
  308. // Set the global SSAO texture and AO Params
  309. cmd.SetGlobalTexture(k_SSAOTextureName, m_SSAOTextureFinalTarget);
  310. cmd.SetGlobalVector(k_SSAOAmbientOcclusionParamName, new Vector4(0f, 0f, 0f, m_CurrentSettings.DirectLightingStrength));
  311. // If true, SSAO pass is inserted after opaque pass and is expected to modulate lighting result now.
  312. if (m_CurrentSettings.AfterOpaque)
  313. {
  314. // This implicitly also bind depth attachment. Explicitly binding m_Renderer.cameraDepthTarget does not work.
  315. cmd.SetRenderTarget(
  316. m_Renderer.cameraColorTarget,
  317. RenderBufferLoadAction.Load,
  318. RenderBufferStoreAction.Store
  319. );
  320. cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_Material, 0, (int)ShaderPasses.AfterOpaque);
  321. }
  322. }
  323. context.ExecuteCommandBuffer(cmd);
  324. CommandBufferPool.Release(cmd);
  325. }
  326. private void Render(CommandBuffer cmd, RenderTargetIdentifier target, ShaderPasses pass)
  327. {
  328. cmd.SetRenderTarget(
  329. target,
  330. RenderBufferLoadAction.DontCare,
  331. RenderBufferStoreAction.Store,
  332. target,
  333. RenderBufferLoadAction.DontCare,
  334. RenderBufferStoreAction.DontCare
  335. );
  336. cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_Material, 0, (int)pass);
  337. }
  338. private void RenderAndSetBaseMap(CommandBuffer cmd, RenderTargetIdentifier baseMap, RenderTargetIdentifier target, ShaderPasses pass)
  339. {
  340. cmd.SetGlobalTexture(s_BaseMapID, baseMap);
  341. Render(cmd, target, pass);
  342. }
  343. /// <inheritdoc/>
  344. public override void OnCameraCleanup(CommandBuffer cmd)
  345. {
  346. if (cmd == null)
  347. {
  348. throw new ArgumentNullException("cmd");
  349. }
  350. if (!m_CurrentSettings.AfterOpaque)
  351. {
  352. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ScreenSpaceOcclusion, false);
  353. }
  354. cmd.ReleaseTemporaryRT(s_SSAOTexture1ID);
  355. cmd.ReleaseTemporaryRT(s_SSAOTexture2ID);
  356. cmd.ReleaseTemporaryRT(s_SSAOTexture3ID);
  357. cmd.ReleaseTemporaryRT(s_SSAOTextureFinalID);
  358. }
  359. }
  360. }
  361. }