MainLightShadowCasterPass.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. using System;
  2. using UnityEngine.Experimental.Rendering;
  3. namespace UnityEngine.Rendering.Universal.Internal
  4. {
  5. /// <summary>
  6. /// Renders a shadow map for the main Light.
  7. /// </summary>
  8. public class MainLightShadowCasterPass : ScriptableRenderPass
  9. {
  10. private static class MainLightShadowConstantBuffer
  11. {
  12. public static int _WorldToShadow;
  13. public static int _ShadowParams;
  14. public static int _CascadeShadowSplitSpheres0;
  15. public static int _CascadeShadowSplitSpheres1;
  16. public static int _CascadeShadowSplitSpheres2;
  17. public static int _CascadeShadowSplitSpheres3;
  18. public static int _CascadeShadowSplitSphereRadii;
  19. public static int _ShadowOffset0;
  20. public static int _ShadowOffset1;
  21. public static int _ShadowOffset2;
  22. public static int _ShadowOffset3;
  23. public static int _ShadowmapSize;
  24. }
  25. const int k_MaxCascades = 4;
  26. const int k_ShadowmapBufferBits = 16;
  27. float m_CascadeBorder;
  28. float m_MaxShadowDistanceSq;
  29. int m_ShadowCasterCascadesCount;
  30. RenderTargetHandle m_MainLightShadowmap;
  31. internal RenderTexture m_MainLightShadowmapTexture;
  32. Matrix4x4[] m_MainLightShadowMatrices;
  33. ShadowSliceData[] m_CascadeSlices;
  34. Vector4[] m_CascadeSplitDistances;
  35. bool m_CreateEmptyShadowmap;
  36. ProfilingSampler m_ProfilingSetupSampler = new ProfilingSampler("Setup Main Shadowmap");
  37. public MainLightShadowCasterPass(RenderPassEvent evt)
  38. {
  39. base.profilingSampler = new ProfilingSampler(nameof(MainLightShadowCasterPass));
  40. renderPassEvent = evt;
  41. m_MainLightShadowMatrices = new Matrix4x4[k_MaxCascades + 1];
  42. m_CascadeSlices = new ShadowSliceData[k_MaxCascades];
  43. m_CascadeSplitDistances = new Vector4[k_MaxCascades];
  44. MainLightShadowConstantBuffer._WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow");
  45. MainLightShadowConstantBuffer._ShadowParams = Shader.PropertyToID("_MainLightShadowParams");
  46. MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0");
  47. MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1");
  48. MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2");
  49. MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3 = Shader.PropertyToID("_CascadeShadowSplitSpheres3");
  50. MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii = Shader.PropertyToID("_CascadeShadowSplitSphereRadii");
  51. MainLightShadowConstantBuffer._ShadowOffset0 = Shader.PropertyToID("_MainLightShadowOffset0");
  52. MainLightShadowConstantBuffer._ShadowOffset1 = Shader.PropertyToID("_MainLightShadowOffset1");
  53. MainLightShadowConstantBuffer._ShadowOffset2 = Shader.PropertyToID("_MainLightShadowOffset2");
  54. MainLightShadowConstantBuffer._ShadowOffset3 = Shader.PropertyToID("_MainLightShadowOffset3");
  55. MainLightShadowConstantBuffer._ShadowmapSize = Shader.PropertyToID("_MainLightShadowmapSize");
  56. m_MainLightShadowmap.Init("_MainLightShadowmapTexture");
  57. }
  58. public bool Setup(ref RenderingData renderingData)
  59. {
  60. using var profScope = new ProfilingScope(null, m_ProfilingSetupSampler);
  61. if (!renderingData.shadowData.supportsMainLightShadows)
  62. return SetupForEmptyRendering(ref renderingData);
  63. Clear();
  64. int shadowLightIndex = renderingData.lightData.mainLightIndex;
  65. if (shadowLightIndex == -1)
  66. return SetupForEmptyRendering(ref renderingData);
  67. VisibleLight shadowLight = renderingData.lightData.visibleLights[shadowLightIndex];
  68. Light light = shadowLight.light;
  69. if (light.shadows == LightShadows.None)
  70. return SetupForEmptyRendering(ref renderingData);
  71. if (shadowLight.lightType != LightType.Directional)
  72. {
  73. Debug.LogWarning("Only directional lights are supported as main light.");
  74. }
  75. Bounds bounds;
  76. if (!renderingData.cullResults.GetShadowCasterBounds(shadowLightIndex, out bounds))
  77. return SetupForEmptyRendering(ref renderingData);
  78. m_ShadowCasterCascadesCount = renderingData.shadowData.mainLightShadowCascadesCount;
  79. int shadowResolution = ShadowUtils.GetMaxTileResolutionInAtlas(renderingData.shadowData.mainLightShadowmapWidth,
  80. renderingData.shadowData.mainLightShadowmapHeight, m_ShadowCasterCascadesCount);
  81. renderTargetWidth = renderingData.shadowData.mainLightShadowmapWidth;
  82. renderTargetHeight = (m_ShadowCasterCascadesCount == 2) ?
  83. renderingData.shadowData.mainLightShadowmapHeight >> 1 :
  84. renderingData.shadowData.mainLightShadowmapHeight;
  85. for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
  86. {
  87. bool success = ShadowUtils.ExtractDirectionalLightMatrix(ref renderingData.cullResults, ref renderingData.shadowData,
  88. shadowLightIndex, cascadeIndex, renderTargetWidth, renderTargetHeight, shadowResolution, light.shadowNearPlane,
  89. out m_CascadeSplitDistances[cascadeIndex], out m_CascadeSlices[cascadeIndex]);
  90. if (!success)
  91. return SetupForEmptyRendering(ref renderingData);
  92. }
  93. m_MainLightShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(renderTargetWidth, renderTargetHeight, k_ShadowmapBufferBits);
  94. m_MaxShadowDistanceSq = renderingData.cameraData.maxShadowDistance * renderingData.cameraData.maxShadowDistance;
  95. m_CascadeBorder = renderingData.shadowData.mainLightShadowCascadeBorder;
  96. m_CreateEmptyShadowmap = false;
  97. useNativeRenderPass = true;
  98. return true;
  99. }
  100. bool SetupForEmptyRendering(ref RenderingData renderingData)
  101. {
  102. if (!renderingData.cameraData.renderer.stripShadowsOffVariants)
  103. return false;
  104. m_MainLightShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(1, 1, k_ShadowmapBufferBits);
  105. m_CreateEmptyShadowmap = true;
  106. useNativeRenderPass = false;
  107. return true;
  108. }
  109. public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
  110. {
  111. ConfigureTarget(new RenderTargetIdentifier(m_MainLightShadowmapTexture), m_MainLightShadowmapTexture.depthStencilFormat, renderTargetWidth, renderTargetHeight, 1, true);
  112. ConfigureClear(ClearFlag.All, Color.black);
  113. }
  114. /// <inheritdoc/>
  115. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
  116. {
  117. if (m_CreateEmptyShadowmap)
  118. {
  119. SetEmptyMainLightCascadeShadowmap(ref context);
  120. return;
  121. }
  122. RenderMainLightCascadeShadowmap(ref context, ref renderingData.cullResults, ref renderingData.lightData, ref renderingData.shadowData);
  123. }
  124. /// <inheritdoc/>
  125. public override void OnCameraCleanup(CommandBuffer cmd)
  126. {
  127. if (cmd == null)
  128. throw new ArgumentNullException("cmd");
  129. if (m_MainLightShadowmapTexture)
  130. {
  131. RenderTexture.ReleaseTemporary(m_MainLightShadowmapTexture);
  132. m_MainLightShadowmapTexture = null;
  133. }
  134. }
  135. void Clear()
  136. {
  137. m_MainLightShadowmapTexture = null;
  138. for (int i = 0; i < m_MainLightShadowMatrices.Length; ++i)
  139. m_MainLightShadowMatrices[i] = Matrix4x4.identity;
  140. for (int i = 0; i < m_CascadeSplitDistances.Length; ++i)
  141. m_CascadeSplitDistances[i] = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
  142. for (int i = 0; i < m_CascadeSlices.Length; ++i)
  143. m_CascadeSlices[i].Clear();
  144. }
  145. void SetEmptyMainLightCascadeShadowmap(ref ScriptableRenderContext context)
  146. {
  147. CommandBuffer cmd = CommandBufferPool.Get();
  148. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, true);
  149. cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture);
  150. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams,
  151. new Vector4(1, 0, 1, 0));
  152. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize,
  153. new Vector4(1f / m_MainLightShadowmapTexture.width, 1f / m_MainLightShadowmapTexture.height, m_MainLightShadowmapTexture.width, m_MainLightShadowmapTexture.height));
  154. context.ExecuteCommandBuffer(cmd);
  155. CommandBufferPool.Release(cmd);
  156. }
  157. void RenderMainLightCascadeShadowmap(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData)
  158. {
  159. int shadowLightIndex = lightData.mainLightIndex;
  160. if (shadowLightIndex == -1)
  161. return;
  162. VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
  163. // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
  164. // Currently there's an issue which results in mismatched markers.
  165. CommandBuffer cmd = CommandBufferPool.Get();
  166. using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.MainLightShadow)))
  167. {
  168. var settings = new ShadowDrawingSettings(cullResults, shadowLightIndex);
  169. settings.useRenderingLayerMaskTest = UniversalRenderPipeline.asset.supportsLightLayers;
  170. for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
  171. {
  172. settings.splitData = m_CascadeSlices[cascadeIndex].splitData;
  173. Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
  174. ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
  175. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, false);
  176. ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_CascadeSlices[cascadeIndex],
  177. ref settings, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
  178. }
  179. shadowData.isKeywordSoftShadowsEnabled = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows;
  180. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, shadowData.mainLightShadowCascadesCount == 1);
  181. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1);
  182. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, shadowData.isKeywordSoftShadowsEnabled);
  183. SetupMainLightShadowReceiverConstants(cmd, shadowLight, shadowData.supportsSoftShadows);
  184. }
  185. context.ExecuteCommandBuffer(cmd);
  186. CommandBufferPool.Release(cmd);
  187. }
  188. void SetupMainLightShadowReceiverConstants(CommandBuffer cmd, VisibleLight shadowLight, bool supportsSoftShadows)
  189. {
  190. Light light = shadowLight.light;
  191. bool softShadows = shadowLight.light.shadows == LightShadows.Soft && supportsSoftShadows;
  192. int cascadeCount = m_ShadowCasterCascadesCount;
  193. for (int i = 0; i < cascadeCount; ++i)
  194. m_MainLightShadowMatrices[i] = m_CascadeSlices[i].shadowTransform;
  195. // We setup and additional a no-op WorldToShadow matrix in the last index
  196. // because the ComputeCascadeIndex function in Shadows.hlsl can return an index
  197. // out of bounds. (position not inside any cascade) and we want to avoid branching
  198. Matrix4x4 noOpShadowMatrix = Matrix4x4.zero;
  199. noOpShadowMatrix.m22 = (SystemInfo.usesReversedZBuffer) ? 1.0f : 0.0f;
  200. for (int i = cascadeCount; i <= k_MaxCascades; ++i)
  201. m_MainLightShadowMatrices[i] = noOpShadowMatrix;
  202. float invShadowAtlasWidth = 1.0f / renderTargetWidth;
  203. float invShadowAtlasHeight = 1.0f / renderTargetHeight;
  204. float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
  205. float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
  206. float softShadowsProp = softShadows ? 1.0f : 0.0f;
  207. ShadowUtils.GetScaleAndBiasForLinearDistanceFade(m_MaxShadowDistanceSq, m_CascadeBorder, out float shadowFadeScale, out float shadowFadeBias);
  208. cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture);
  209. cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices);
  210. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams,
  211. new Vector4(light.shadowStrength, softShadowsProp, shadowFadeScale, shadowFadeBias));
  212. if (m_ShadowCasterCascadesCount > 1)
  213. {
  214. cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0,
  215. m_CascadeSplitDistances[0]);
  216. cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1,
  217. m_CascadeSplitDistances[1]);
  218. cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2,
  219. m_CascadeSplitDistances[2]);
  220. cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3,
  221. m_CascadeSplitDistances[3]);
  222. cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii, new Vector4(
  223. m_CascadeSplitDistances[0].w * m_CascadeSplitDistances[0].w,
  224. m_CascadeSplitDistances[1].w * m_CascadeSplitDistances[1].w,
  225. m_CascadeSplitDistances[2].w * m_CascadeSplitDistances[2].w,
  226. m_CascadeSplitDistances[3].w * m_CascadeSplitDistances[3].w));
  227. }
  228. // Inside shader soft shadows are controlled through global keyword.
  229. // If any additional light has soft shadows it will force soft shadows on main light too.
  230. // As it is not trivial finding out which additional light has soft shadows, we will pass main light properties if soft shadows are supported.
  231. // This workaround will be removed once we will support soft shadows per light.
  232. if (supportsSoftShadows)
  233. {
  234. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset0,
  235. new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
  236. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset1,
  237. new Vector4(invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
  238. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset2,
  239. new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
  240. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset3,
  241. new Vector4(invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
  242. // Currently only used when !SHADER_API_MOBILE but risky to not set them as it's generic
  243. // enough so custom shaders might use it.
  244. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, new Vector4(invShadowAtlasWidth,
  245. invShadowAtlasHeight,
  246. renderTargetWidth, renderTargetHeight));
  247. }
  248. }
  249. };
  250. }