AdditionalLightsShadowCasterPass.cs 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Collections;
  4. using UnityEngine.Experimental.Rendering;
  5. namespace UnityEngine.Rendering.Universal.Internal
  6. {
  7. /// <summary>
  8. /// Renders a shadow map atlas for additional shadow-casting Lights.
  9. /// </summary>
  10. public partial class AdditionalLightsShadowCasterPass : ScriptableRenderPass
  11. {
  12. private static class AdditionalShadowsConstantBuffer
  13. {
  14. public static int _AdditionalLightsWorldToShadow;
  15. public static int _AdditionalShadowParams;
  16. public static int _AdditionalShadowOffset0;
  17. public static int _AdditionalShadowOffset1;
  18. public static int _AdditionalShadowOffset2;
  19. public static int _AdditionalShadowOffset3;
  20. public static int _AdditionalShadowFadeParams;
  21. public static int _AdditionalShadowmapSize;
  22. }
  23. internal struct ShadowResolutionRequest
  24. {
  25. public int visibleLightIndex;
  26. public int perLightShadowSliceIndex;
  27. public int requestedResolution;
  28. public bool softShadow; // otherwise it's hard-shadow (no filtering)
  29. public bool pointLightShadow; // otherwise it's spot light shadow (1 shadow slice instead of 6)
  30. public int offsetX; // x coordinate of the square area allocated in the atlas for this shadow map
  31. public int offsetY; // y coordinate of the square area allocated in the atlas for this shadow map
  32. public int allocatedResolution; // width of the square area allocated in the atlas for this shadow map
  33. public ShadowResolutionRequest(int _visibleLightIndex, int _perLightShadowSliceIndex, int _requestedResolution, bool _softShadow, bool _pointLightShadow)
  34. {
  35. visibleLightIndex = _visibleLightIndex;
  36. perLightShadowSliceIndex = _perLightShadowSliceIndex;
  37. requestedResolution = _requestedResolution;
  38. softShadow = _softShadow;
  39. pointLightShadow = _pointLightShadow;
  40. offsetX = 0;
  41. offsetY = 0;
  42. allocatedResolution = 0;
  43. }
  44. }
  45. /// <summary>
  46. /// x is used in RenderAdditionalShadowMapAtlas to skip shadow map rendering for non-shadow-casting lights.
  47. /// w is perLightFirstShadowSliceIndex, used in Lighting shader to find if Additional light casts shadows.
  48. /// </summary>
  49. readonly static Vector4 c_DefaultShadowParams = new Vector4(0, 0, 0, -1);
  50. static int m_AdditionalLightsWorldToShadow_SSBO;
  51. static int m_AdditionalShadowParams_SSBO;
  52. bool m_UseStructuredBuffer;
  53. const int k_ShadowmapBufferBits = 16;
  54. private RenderTargetHandle m_AdditionalLightsShadowmap;
  55. internal RenderTexture m_AdditionalLightsShadowmapTexture;
  56. float m_MaxShadowDistanceSq;
  57. float m_CascadeBorder;
  58. ShadowSliceData[] m_AdditionalLightsShadowSlices = null;
  59. int[] m_VisibleLightIndexToAdditionalLightIndex = null; // maps a "global" visible light index (index to renderingData.lightData.visibleLights) to an "additional light index" (index to arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...), or -1 if it is not an additional light (i.e if it is the main light)
  60. int[] m_AdditionalLightIndexToVisibleLightIndex = null; // maps additional light index (index to arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...) to its "global" visible light index (index to renderingData.lightData.visibleLights)
  61. List<int> m_ShadowSliceToAdditionalLightIndex = new List<int>(); // For each shadow slice, store the "additional light indices" of the punctual light that casts it
  62. List<int> m_GlobalShadowSliceIndexToPerLightShadowSliceIndex = new List<int>(); // For each shadow slice, store its "per-light shadow slice index" in the punctual light that casts it (can be up to 5 for point lights)
  63. Vector4[] m_AdditionalLightIndexToShadowParams = null; // per-additional-light shadow info passed to the lighting shader
  64. Matrix4x4[] m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = null; // per-shadow-slice info passed to the lighting shader
  65. List<ShadowResolutionRequest> m_ShadowResolutionRequests = new List<ShadowResolutionRequest>(); // intermediate array used to compute the final resolution of each shadow slice rendered in the frame
  66. float[] m_VisibleLightIndexToCameraSquareDistance = null; // stores for each shadowed additional light its (squared) distance to camera ; used to sub-sort shadow requests according to how close their casting light is
  67. ShadowResolutionRequest[] m_SortedShadowResolutionRequests = null;
  68. int[] m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = null; // for each visible light, store the index of its first shadow slice in m_SortedShadowResolutionRequests (for quicker access)
  69. List<RectInt> m_UnusedAtlasSquareAreas = new List<RectInt>(); // this list tracks space available in the atlas
  70. bool m_CreateEmptyShadowmap;
  71. ProfilingSampler m_ProfilingSetupSampler = new ProfilingSampler("Setup Additional Shadows");
  72. int MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO // keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in Shadows.hlsl
  73. {
  74. get
  75. {
  76. if (UniversalRenderPipeline.maxVisibleAdditionalLights != UniversalRenderPipeline.k_MaxVisibleAdditionalLightsNonMobile)
  77. // Reduce uniform block size on Mobile/GL to avoid shader performance or compilation issues - keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in Shadows.hlsl
  78. return UniversalRenderPipeline.maxVisibleAdditionalLights;
  79. else
  80. return 545; // keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in Shadows.hlsl
  81. }
  82. }
  83. public AdditionalLightsShadowCasterPass(RenderPassEvent evt)
  84. {
  85. base.profilingSampler = new ProfilingSampler(nameof(AdditionalLightsShadowCasterPass));
  86. renderPassEvent = evt;
  87. AdditionalShadowsConstantBuffer._AdditionalLightsWorldToShadow = Shader.PropertyToID("_AdditionalLightsWorldToShadow");
  88. AdditionalShadowsConstantBuffer._AdditionalShadowParams = Shader.PropertyToID("_AdditionalShadowParams");
  89. AdditionalShadowsConstantBuffer._AdditionalShadowOffset0 = Shader.PropertyToID("_AdditionalShadowOffset0");
  90. AdditionalShadowsConstantBuffer._AdditionalShadowOffset1 = Shader.PropertyToID("_AdditionalShadowOffset1");
  91. AdditionalShadowsConstantBuffer._AdditionalShadowOffset2 = Shader.PropertyToID("_AdditionalShadowOffset2");
  92. AdditionalShadowsConstantBuffer._AdditionalShadowOffset3 = Shader.PropertyToID("_AdditionalShadowOffset3");
  93. AdditionalShadowsConstantBuffer._AdditionalShadowFadeParams = Shader.PropertyToID("_AdditionalShadowFadeParams");
  94. AdditionalShadowsConstantBuffer._AdditionalShadowmapSize = Shader.PropertyToID("_AdditionalShadowmapSize");
  95. m_AdditionalLightsShadowmap.Init("_AdditionalLightsShadowmapTexture");
  96. m_AdditionalLightsWorldToShadow_SSBO = Shader.PropertyToID("_AdditionalLightsWorldToShadow_SSBO");
  97. m_AdditionalShadowParams_SSBO = Shader.PropertyToID("_AdditionalShadowParams_SSBO");
  98. m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
  99. // Preallocated a fixed size. CommandBuffer.SetGlobal* does allow this data to grow.
  100. int maxVisibleAdditionalLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
  101. const int maxMainLights = 1;
  102. int maxVisibleLights = UniversalRenderPipeline.maxVisibleAdditionalLights + maxMainLights;
  103. int maxAdditionalLightShadowParams = m_UseStructuredBuffer ? maxVisibleLights : Math.Min(maxVisibleLights, UniversalRenderPipeline.maxVisibleAdditionalLights);
  104. // These array sizes should be as big as ScriptableCullingParameters.maximumVisibleLights (that is defined during ScriptableRenderer.SetupCullingParameters).
  105. // We initialize these array sizes with the number of visible lights allowed by the UniversalRenderer.
  106. // The number of visible lights can become much higher when using the Deferred rendering path, we resize the arrays during Setup() if required.
  107. m_AdditionalLightIndexToVisibleLightIndex = new int[maxAdditionalLightShadowParams];
  108. m_VisibleLightIndexToAdditionalLightIndex = new int[maxVisibleLights];
  109. m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = new int[maxVisibleLights];
  110. m_AdditionalLightIndexToShadowParams = new Vector4[maxAdditionalLightShadowParams];
  111. m_VisibleLightIndexToCameraSquareDistance = new float[maxVisibleLights];
  112. if (!m_UseStructuredBuffer)
  113. {
  114. // Uniform buffers are faster on some platforms, but they have stricter size limitations
  115. m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO];
  116. m_UnusedAtlasSquareAreas.Capacity = MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO;
  117. m_ShadowResolutionRequests.Capacity = MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO;
  118. }
  119. }
  120. private int GetPunctualLightShadowSlicesCount(in LightType lightType)
  121. {
  122. switch (lightType)
  123. {
  124. case LightType.Spot:
  125. return 1;
  126. case LightType.Point:
  127. return 6;
  128. default:
  129. return 0;
  130. }
  131. }
  132. // Magic numbers used to identify light type when rendering shadow receiver.
  133. // Keep in sync with AdditionalLightRealtimeShadow code in com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
  134. private const float LightTypeIdentifierInShadowParams_Spot = 0;
  135. private const float LightTypeIdentifierInShadowParams_Point = 1;
  136. // Returns the guard angle that must be added to a frustum angle covering a projection map of resolution sliceResolutionInTexels,
  137. // in order to also cover a guard band of size guardBandSizeInTexels around the projection map.
  138. // Formula illustrated in https://i.ibb.co/wpW5Mnf/Calc-Guard-Angle.png
  139. internal static float CalcGuardAngle(float frustumAngleInDegrees, float guardBandSizeInTexels, float sliceResolutionInTexels)
  140. {
  141. float frustumAngle = frustumAngleInDegrees * Mathf.Deg2Rad;
  142. float halfFrustumAngle = frustumAngle / 2;
  143. float tanHalfFrustumAngle = Mathf.Tan(halfFrustumAngle);
  144. float halfSliceResolution = sliceResolutionInTexels / 2;
  145. float halfGuardBand = guardBandSizeInTexels / 2;
  146. float factorBetweenAngleTangents = 1 + halfGuardBand / halfSliceResolution;
  147. float tanHalfGuardAnglePlusHalfFrustumAngle = tanHalfFrustumAngle * factorBetweenAngleTangents;
  148. float halfGuardAnglePlusHalfFrustumAngle = Mathf.Atan(tanHalfGuardAnglePlusHalfFrustumAngle);
  149. float halfGuardAngleInRadian = halfGuardAnglePlusHalfFrustumAngle - halfFrustumAngle;
  150. float guardAngleInRadian = 2 * halfGuardAngleInRadian;
  151. float guardAngleInDegree = guardAngleInRadian * Mathf.Rad2Deg;
  152. return guardAngleInDegree;
  153. }
  154. private const int kMinimumPunctualLightHardShadowResolution = 8;
  155. private const int kMinimumPunctualLightSoftShadowResolution = 16;
  156. // Minimal shadow map resolution required to have meaningful shadows visible during lighting
  157. int MinimalPunctualLightShadowResolution(bool softShadow)
  158. {
  159. return softShadow ? kMinimumPunctualLightSoftShadowResolution : kMinimumPunctualLightHardShadowResolution;
  160. }
  161. static bool m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = false;
  162. static bool m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = false;
  163. // Returns the guard angle that must be added to a point light shadow face frustum angle
  164. // in order to avoid shadows missing at the boundaries between cube faces.
  165. internal static float GetPointLightShadowFrustumFovBiasInDegrees(int shadowSliceResolution, bool shadowFiltering)
  166. {
  167. // Commented-out code below uses the theoretical formula to compute the required guard angle based on the number of additional
  168. // texels that the projection should cover. It is close to HDRP's HDShadowUtils.CalcGuardAnglePerspective method.
  169. // However, due to precision issues or other filterings performed at lighting for example, this formula also still requires a fudge factor.
  170. // Since we only handle a fixed number of resolutions, we use empirical values instead.
  171. #if false
  172. float fudgeFactor = 1.5f;
  173. return fudgeFactor * CalcGuardAngle(90, shadowFiltering ? 5 : 1, shadowSliceResolution);
  174. #endif
  175. float fovBias = 4.00f;
  176. // Empirical value found to remove gaps between point light shadow faces in test scenes.
  177. // We can see that the guard angle is roughly proportional to the inverse of resolution https://docs.google.com/spreadsheets/d/1QrIZJn18LxVKq2-K1XS4EFRZcZdZOJTTKKhDN8Z1b_s
  178. if (shadowSliceResolution <= kMinimumPunctualLightHardShadowResolution)
  179. {
  180. if (!m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall)
  181. {
  182. Debug.LogWarning("Too many additional punctual lights shadows, increase shadow atlas size or remove some shadowed lights");
  183. m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = true; // Only output this once per shadow requests configuration
  184. }
  185. }
  186. else if (shadowSliceResolution <= 16)
  187. fovBias = 43.0f;
  188. else if (shadowSliceResolution <= 32)
  189. fovBias = 18.55f;
  190. else if (shadowSliceResolution <= 64)
  191. fovBias = 8.63f;
  192. else if (shadowSliceResolution <= 128)
  193. fovBias = 4.13f;
  194. else if (shadowSliceResolution <= 256)
  195. fovBias = 2.03f;
  196. else if (shadowSliceResolution <= 512)
  197. fovBias = 1.00f;
  198. else if (shadowSliceResolution <= 1024)
  199. fovBias = 0.50f;
  200. if (shadowFiltering)
  201. {
  202. if (shadowSliceResolution <= kMinimumPunctualLightSoftShadowResolution)
  203. {
  204. if (!m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall)
  205. {
  206. Debug.LogWarning("Too many additional punctual lights shadows to use Soft Shadows. Increase shadow atlas size, remove some shadowed lights or use Hard Shadows.");
  207. // With such small resolutions no fovBias can give good visual results
  208. m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = true; // Only output this once per shadow requests configuration
  209. }
  210. }
  211. else if (shadowSliceResolution <= 32)
  212. fovBias += 9.35f;
  213. else if (shadowSliceResolution <= 64)
  214. fovBias += 4.07f;
  215. else if (shadowSliceResolution <= 128)
  216. fovBias += 1.77f;
  217. else if (shadowSliceResolution <= 256)
  218. fovBias += 0.85f;
  219. else if (shadowSliceResolution <= 512)
  220. fovBias += 0.39f;
  221. else if (shadowSliceResolution <= 1024)
  222. fovBias += 0.17f;
  223. // These values were verified to work on untethered devices for which m_SupportsBoxFilterForShadows is true.
  224. // TODO: Investigate finer-tuned values for those platforms. Soft shadows are implemented differently for them.
  225. }
  226. return fovBias;
  227. }
  228. bool m_IssuedMessageAboutShadowSlicesTooMany = false;
  229. // Adapted from InsertionSort() in com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs
  230. // Sort array in decreasing requestedResolution order,
  231. // sub-sorting in "HardShadow > SoftShadow" and then "Spot > Point", i.e place last requests that will be removed in priority to make room for the others, because their resolution is too small to produce good-looking shadows ; or because they take relatively more space in the atlas )
  232. // sub-sub-sorting in light distance to camera
  233. // then grouping in increasing visibleIndex (and sub-sorting each group in ShadowSliceIndex order)
  234. internal void InsertionSort(ShadowResolutionRequest[] array, int startIndex, int lastIndex)
  235. {
  236. int i = startIndex + 1;
  237. while (i < lastIndex)
  238. {
  239. var curr = array[i];
  240. int j = i - 1;
  241. // Sort in priority order
  242. while ((j >= 0) && ((curr.requestedResolution > array[j].requestedResolution)
  243. || (curr.requestedResolution == array[j].requestedResolution && !curr.softShadow && array[j].softShadow)
  244. || (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && !curr.pointLightShadow && array[j].pointLightShadow)
  245. || (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && curr.pointLightShadow == array[j].pointLightShadow && m_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] < m_VisibleLightIndexToCameraSquareDistance[array[j].visibleLightIndex])
  246. || (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && curr.pointLightShadow == array[j].pointLightShadow && m_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == m_VisibleLightIndexToCameraSquareDistance[array[j].visibleLightIndex] && curr.visibleLightIndex < array[j].visibleLightIndex)
  247. || (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && curr.pointLightShadow == array[j].pointLightShadow && m_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == m_VisibleLightIndexToCameraSquareDistance[array[j].visibleLightIndex] && curr.visibleLightIndex == array[j].visibleLightIndex && curr.perLightShadowSliceIndex < array[j].perLightShadowSliceIndex)))
  248. {
  249. array[j + 1] = array[j];
  250. j--;
  251. }
  252. array[j + 1] = curr;
  253. i++;
  254. }
  255. }
  256. int EstimateScaleFactorNeededToFitAllShadowsInAtlas(in ShadowResolutionRequest[] shadowResolutionRequests, int endIndex, int atlasWidth)
  257. {
  258. long totalTexelsInShadowAtlas = atlasWidth * atlasWidth;
  259. long totalTexelsInShadowRequests = 0;
  260. for (int shadowRequestIndex = 0; shadowRequestIndex < endIndex; ++shadowRequestIndex)
  261. totalTexelsInShadowRequests += shadowResolutionRequests[shadowRequestIndex].requestedResolution * shadowResolutionRequests[shadowRequestIndex].requestedResolution;
  262. int estimatedScaleFactor = 1;
  263. while (totalTexelsInShadowRequests > totalTexelsInShadowAtlas * estimatedScaleFactor * estimatedScaleFactor)
  264. estimatedScaleFactor *= 2;
  265. return estimatedScaleFactor;
  266. }
  267. // Assigns to each of the first totalShadowSlicesCount items in m_SortedShadowResolutionRequests a location in the shadow atlas based on requested resolutions.
  268. // If necessary, scales down shadow maps active in the frame, to make all of them fit in the atlas.
  269. void AtlasLayout(int atlasSize, int totalShadowSlicesCount, int estimatedScaleFactor)
  270. {
  271. bool allShadowSlicesFitInAtlas = false;
  272. bool tooManyShadows = false;
  273. int shadowSlicesScaleFactor = estimatedScaleFactor;
  274. while (!allShadowSlicesFitInAtlas && !tooManyShadows)
  275. {
  276. m_UnusedAtlasSquareAreas.Clear();
  277. m_UnusedAtlasSquareAreas.Add(new RectInt(0, 0, atlasSize, atlasSize));
  278. allShadowSlicesFitInAtlas = true;
  279. for (int shadowRequestIndex = 0; shadowRequestIndex < totalShadowSlicesCount; ++shadowRequestIndex)
  280. {
  281. var resolution = m_SortedShadowResolutionRequests[shadowRequestIndex].requestedResolution / shadowSlicesScaleFactor;
  282. if (resolution < MinimalPunctualLightShadowResolution(m_SortedShadowResolutionRequests[shadowRequestIndex].softShadow))
  283. {
  284. tooManyShadows = true;
  285. break;
  286. }
  287. bool foundSpaceInAtlas = false;
  288. // Try to find free space in the atlas
  289. for (int unusedAtlasSquareAreaIndex = 0; unusedAtlasSquareAreaIndex < m_UnusedAtlasSquareAreas.Count; ++unusedAtlasSquareAreaIndex)
  290. {
  291. var atlasArea = m_UnusedAtlasSquareAreas[unusedAtlasSquareAreaIndex];
  292. var atlasAreaWidth = atlasArea.width;
  293. var atlasAreaHeight = atlasArea.height;
  294. var atlasAreaX = atlasArea.x;
  295. var atlasAreaY = atlasArea.y;
  296. if (atlasAreaWidth >= resolution)
  297. {
  298. // we can use this atlas area for the shadow request
  299. m_SortedShadowResolutionRequests[shadowRequestIndex].offsetX = atlasAreaX;
  300. m_SortedShadowResolutionRequests[shadowRequestIndex].offsetY = atlasAreaY;
  301. m_SortedShadowResolutionRequests[shadowRequestIndex].allocatedResolution = resolution;
  302. // this atlas space is not available anymore, so remove it from the list
  303. m_UnusedAtlasSquareAreas.RemoveAt(unusedAtlasSquareAreaIndex);
  304. // make sure to split space so that the rest of this square area can be used
  305. int remainingShadowRequestsCount = totalShadowSlicesCount - shadowRequestIndex - 1; // (no need to add more than that)
  306. int newSquareAreasCount = 0;
  307. int newSquareAreaWidth = resolution; // we split the area in squares of same size
  308. int newSquareAreaHeight = resolution;
  309. var newSquareAreaX = atlasAreaX;
  310. var newSquareAreaY = atlasAreaY;
  311. while (newSquareAreasCount < remainingShadowRequestsCount)
  312. {
  313. newSquareAreaX += newSquareAreaWidth;
  314. if (newSquareAreaX + newSquareAreaWidth > (atlasAreaX + atlasAreaWidth))
  315. {
  316. newSquareAreaX = atlasAreaX;
  317. newSquareAreaY += newSquareAreaHeight;
  318. if (newSquareAreaY + newSquareAreaHeight > (atlasAreaY + atlasAreaHeight))
  319. break;
  320. }
  321. // replace the space we removed previously by new smaller squares (inserting them in this order ensures shadow maps will be packed at the side of the atlas, without gaps)
  322. m_UnusedAtlasSquareAreas.Insert(unusedAtlasSquareAreaIndex + newSquareAreasCount, new RectInt(newSquareAreaX, newSquareAreaY, newSquareAreaWidth, newSquareAreaHeight));
  323. ++newSquareAreasCount;
  324. }
  325. foundSpaceInAtlas = true;
  326. break;
  327. }
  328. }
  329. if (!foundSpaceInAtlas)
  330. {
  331. allShadowSlicesFitInAtlas = false;
  332. break;
  333. }
  334. }
  335. if (!allShadowSlicesFitInAtlas && !tooManyShadows)
  336. shadowSlicesScaleFactor *= 2;
  337. }
  338. if (!m_IssuedMessageAboutShadowMapsTooBig && tooManyShadows)
  339. {
  340. Debug.LogWarning($"Too many additional punctual lights shadows. URP tried reducing shadow resolutions by {shadowSlicesScaleFactor} but it was still too much. Increase shadow atlas size, decrease big shadow resolutions, or reduce the number of shadow maps active in the same frame (currently was {totalShadowSlicesCount}).");
  341. m_IssuedMessageAboutShadowMapsTooBig = true; // Only output this once per shadow requests configuration
  342. }
  343. if (!m_IssuedMessageAboutShadowMapsRescale && shadowSlicesScaleFactor > 1)
  344. {
  345. Debug.Log($"Reduced additional punctual light shadows resolution by {shadowSlicesScaleFactor} to make {totalShadowSlicesCount} shadow maps fit in the {atlasSize}x{atlasSize} shadow atlas. To avoid this, increase shadow atlas size, decrease big shadow resolutions, or reduce the number of shadow maps active in the same frame");
  346. m_IssuedMessageAboutShadowMapsRescale = true; // Only output this once per shadow requests configuration
  347. }
  348. }
  349. bool m_IssuedMessageAboutShadowMapsRescale = false;
  350. bool m_IssuedMessageAboutShadowMapsTooBig = false;
  351. bool m_IssuedMessageAboutRemovedShadowSlices = false;
  352. Dictionary<int, ulong> m_ShadowRequestsHashes = new Dictionary<int, ulong>(); // used to keep track of changes in the shadow requests and shadow atlas configuration (per camera)
  353. ulong ResolutionLog2ForHash(int resolution)
  354. {
  355. switch (resolution)
  356. {
  357. case 4096: return 12;
  358. case 2048: return 11;
  359. case 1024: return 10;
  360. case 0512: return 09;
  361. }
  362. return 08;
  363. }
  364. ulong ComputeShadowRequestHash(ref RenderingData renderingData)
  365. {
  366. ulong numberOfShadowedPointLights = 0;
  367. ulong numberOfSoftShadowedLights = 0;
  368. ulong numberOfShadowsWithResolution0128 = 0;
  369. ulong numberOfShadowsWithResolution0256 = 0;
  370. ulong numberOfShadowsWithResolution0512 = 0;
  371. ulong numberOfShadowsWithResolution1024 = 0;
  372. ulong numberOfShadowsWithResolution2048 = 0;
  373. ulong numberOfShadowsWithResolution4096 = 0;
  374. var visibleLights = renderingData.lightData.visibleLights;
  375. for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
  376. {
  377. if (!IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
  378. continue;
  379. if (visibleLights[visibleLightIndex].lightType == LightType.Point)
  380. ++numberOfShadowedPointLights;
  381. if (visibleLights[visibleLightIndex].light.shadows == LightShadows.Soft)
  382. ++numberOfSoftShadowedLights;
  383. if (renderingData.shadowData.resolution[visibleLightIndex] == 0128)
  384. ++numberOfShadowsWithResolution0128;
  385. if (renderingData.shadowData.resolution[visibleLightIndex] == 0256)
  386. ++numberOfShadowsWithResolution0256;
  387. if (renderingData.shadowData.resolution[visibleLightIndex] == 0512)
  388. ++numberOfShadowsWithResolution0512;
  389. if (renderingData.shadowData.resolution[visibleLightIndex] == 1024)
  390. ++numberOfShadowsWithResolution1024;
  391. if (renderingData.shadowData.resolution[visibleLightIndex] == 2048)
  392. ++numberOfShadowsWithResolution2048;
  393. if (renderingData.shadowData.resolution[visibleLightIndex] == 4096)
  394. ++numberOfShadowsWithResolution4096;
  395. }
  396. ulong shadowRequestsHash = ResolutionLog2ForHash(renderingData.shadowData.additionalLightsShadowmapWidth) - 8; // bits [00~02]
  397. shadowRequestsHash |= numberOfShadowedPointLights << 03; // bits [03~10]
  398. shadowRequestsHash |= numberOfSoftShadowedLights << 11; // bits [11~18]
  399. shadowRequestsHash |= numberOfShadowsWithResolution0128 << 19; // bits [19~26]
  400. shadowRequestsHash |= numberOfShadowsWithResolution0256 << 27; // bits [27~34]
  401. shadowRequestsHash |= numberOfShadowsWithResolution0512 << 35; // bits [35~42]
  402. shadowRequestsHash |= numberOfShadowsWithResolution1024 << 43; // bits [43~49]
  403. shadowRequestsHash |= numberOfShadowsWithResolution2048 << 50; // bits [50~56]
  404. shadowRequestsHash |= numberOfShadowsWithResolution4096 << 57; // bits [57~63]
  405. return shadowRequestsHash;
  406. }
  407. public bool Setup(ref RenderingData renderingData)
  408. {
  409. using var profScope = new ProfilingScope(null, m_ProfilingSetupSampler);
  410. Clear();
  411. renderTargetWidth = renderingData.shadowData.additionalLightsShadowmapWidth;
  412. renderTargetHeight = renderingData.shadowData.additionalLightsShadowmapHeight;
  413. var visibleLights = renderingData.lightData.visibleLights;
  414. int additionalLightsCount = renderingData.lightData.additionalLightsCount;
  415. int atlasWidth = renderingData.shadowData.additionalLightsShadowmapWidth;
  416. int totalShadowResolutionRequestsCount = 0; // Number of shadow slices that we would need for all shadowed additional (punctual) lights in the scene. We might have to ignore some of those requests if they do not fit in the shadow atlas.
  417. m_ShadowResolutionRequests.Clear();
  418. // Check changes in the shadow requests and shadow atlas configuration - compute shadow request/configuration hash
  419. if (!renderingData.cameraData.isPreviewCamera)
  420. {
  421. ulong newShadowRequestHash = ComputeShadowRequestHash(ref renderingData);
  422. ulong oldShadowRequestHash = 0;
  423. m_ShadowRequestsHashes.TryGetValue(renderingData.cameraData.camera.GetHashCode(), out oldShadowRequestHash);
  424. if (oldShadowRequestHash != newShadowRequestHash)
  425. {
  426. m_ShadowRequestsHashes[renderingData.cameraData.camera.GetHashCode()] = newShadowRequestHash;
  427. // congif changed ; reset error message flags as we might need to issue those messages again
  428. m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = false;
  429. m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = false;
  430. m_IssuedMessageAboutShadowMapsRescale = false;
  431. m_IssuedMessageAboutShadowMapsTooBig = false;
  432. m_IssuedMessageAboutShadowSlicesTooMany = false;
  433. m_IssuedMessageAboutRemovedShadowSlices = false;
  434. }
  435. }
  436. if (m_VisibleLightIndexToAdditionalLightIndex.Length < visibleLights.Length)
  437. {
  438. // Array "visibleLights" is returned by ScriptableRenderContext.Cull()
  439. // The maximum number of "visibleLights" that ScriptableRenderContext.Cull() should return, is defined by parameter ScriptableCullingParameters.maximumVisibleLights
  440. // Universal RP sets this "ScriptableCullingParameters.maximumVisibleLights" value during ScriptableRenderer.SetupCullingParameters.
  441. // When using Deferred rendering, it is possible to specify a very high number of visible lights.
  442. m_VisibleLightIndexToAdditionalLightIndex = new int[visibleLights.Length];
  443. m_VisibleLightIndexToCameraSquareDistance = new float[visibleLights.Length];
  444. m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = new int[visibleLights.Length];
  445. }
  446. int maxAdditionalLightShadowParams = m_UseStructuredBuffer ? visibleLights.Length : Math.Min(visibleLights.Length, UniversalRenderPipeline.maxVisibleAdditionalLights);
  447. if (m_AdditionalLightIndexToVisibleLightIndex.Length < maxAdditionalLightShadowParams)
  448. {
  449. m_AdditionalLightIndexToVisibleLightIndex = new int[maxAdditionalLightShadowParams];
  450. m_AdditionalLightIndexToShadowParams = new Vector4[maxAdditionalLightShadowParams];
  451. }
  452. // reset m_VisibleLightIndexClosenessToCamera
  453. for (int visibleLightIndex = 0; visibleLightIndex < m_VisibleLightIndexToCameraSquareDistance.Length; ++visibleLightIndex)
  454. m_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = float.MaxValue;
  455. for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
  456. {
  457. if (visibleLightIndex == renderingData.lightData.mainLightIndex)
  458. // Skip main directional light as it is not packed into the shadow atlas
  459. continue;
  460. if (IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
  461. {
  462. int shadowSlicesCountForThisLight = GetPunctualLightShadowSlicesCount(visibleLights[visibleLightIndex].lightType);
  463. totalShadowResolutionRequestsCount += shadowSlicesCountForThisLight;
  464. for (int perLightShadowSliceIndex = 0; perLightShadowSliceIndex < shadowSlicesCountForThisLight; ++perLightShadowSliceIndex)
  465. {
  466. m_ShadowResolutionRequests.Add(new ShadowResolutionRequest(visibleLightIndex, perLightShadowSliceIndex, renderingData.shadowData.resolution[visibleLightIndex],
  467. (visibleLights[visibleLightIndex].light.shadows == LightShadows.Soft), (visibleLights[visibleLightIndex].lightType == LightType.Point)));
  468. }
  469. // mark this light as casting shadows
  470. m_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = (renderingData.cameraData.camera.transform.position - visibleLights[visibleLightIndex].light.transform.position).sqrMagnitude;
  471. }
  472. }
  473. if (m_SortedShadowResolutionRequests == null || m_SortedShadowResolutionRequests.Length < totalShadowResolutionRequestsCount)
  474. m_SortedShadowResolutionRequests = new ShadowResolutionRequest[totalShadowResolutionRequestsCount];
  475. for (int shadowRequestIndex = 0; shadowRequestIndex < m_ShadowResolutionRequests.Count; ++shadowRequestIndex)
  476. m_SortedShadowResolutionRequests[shadowRequestIndex] = m_ShadowResolutionRequests[shadowRequestIndex];
  477. for (int sortedArrayIndex = totalShadowResolutionRequestsCount; sortedArrayIndex < m_SortedShadowResolutionRequests.Length; ++sortedArrayIndex)
  478. m_SortedShadowResolutionRequests[sortedArrayIndex].requestedResolution = 0; // reset unused entries
  479. InsertionSort(m_SortedShadowResolutionRequests, 0, totalShadowResolutionRequestsCount);
  480. // To avoid visual artifacts when there is not enough place in the atlas, we remove shadow slices that would be allocated a too small resolution.
  481. // When not using structured buffers, m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length maps to _AdditionalLightsWorldToShadow in Shadows.hlsl
  482. // In that case we have to limit its size because uniform buffers cannot be higher than 64kb for some platforms.
  483. int totalShadowSlicesCount = m_UseStructuredBuffer ? totalShadowResolutionRequestsCount : Math.Min(totalShadowResolutionRequestsCount, MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO); // Number of shadow slices that we will actually be able to fit in the shadow atlas without causing visual artifacts.
  484. // Find biggest end index in m_SortedShadowResolutionRequests array, under which all shadow requests can be allocated a big enough shadow atlas slot, to not cause rendering artifacts
  485. bool allShadowsAfterStartIndexHaveEnoughResolution = false;
  486. int estimatedScaleFactor = 1;
  487. while (!allShadowsAfterStartIndexHaveEnoughResolution && totalShadowSlicesCount > 0)
  488. {
  489. estimatedScaleFactor = EstimateScaleFactorNeededToFitAllShadowsInAtlas(m_SortedShadowResolutionRequests, totalShadowSlicesCount, atlasWidth);
  490. // check if resolution of the least priority shadow slice request would be acceptable
  491. if (m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1].requestedResolution >= estimatedScaleFactor * MinimalPunctualLightShadowResolution(m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1].softShadow))
  492. allShadowsAfterStartIndexHaveEnoughResolution = true;
  493. else // Skip shadow requests for this light ; their resolution is too small to look any good
  494. totalShadowSlicesCount -= GetPunctualLightShadowSlicesCount(m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1].pointLightShadow ? LightType.Point : LightType.Spot);
  495. }
  496. if (totalShadowSlicesCount < totalShadowResolutionRequestsCount)
  497. {
  498. if (!m_IssuedMessageAboutRemovedShadowSlices)
  499. {
  500. Debug.LogWarning($"Too many additional punctual lights shadows to look good, URP removed {totalShadowResolutionRequestsCount - totalShadowSlicesCount } shadow maps to make the others fit in the shadow atlas. To avoid this, increase shadow atlas size, remove some shadowed lights, replace soft shadows by hard shadows ; or replace point lights by spot lights");
  501. m_IssuedMessageAboutRemovedShadowSlices = true; // Only output this once per shadow requests configuration
  502. }
  503. }
  504. for (int sortedArrayIndex = totalShadowSlicesCount; sortedArrayIndex < m_SortedShadowResolutionRequests.Length; ++sortedArrayIndex)
  505. m_SortedShadowResolutionRequests[sortedArrayIndex].requestedResolution = 0; // Reset entries that we cannot fit in the atlas
  506. // Reset the reverse lookup array
  507. for (int visibleLightIndex = 0; visibleLightIndex < m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex.Length; ++visibleLightIndex)
  508. m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] = -1;
  509. // Update the reverse lookup array (starting from the end of the array, in order to use index of slice#0 in case a same visibleLight has several shadowSlices)
  510. for (int sortedArrayIndex = totalShadowSlicesCount - 1; sortedArrayIndex >= 0; --sortedArrayIndex)
  511. m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[m_SortedShadowResolutionRequests[sortedArrayIndex].visibleLightIndex] = sortedArrayIndex;
  512. AtlasLayout(atlasWidth, totalShadowSlicesCount, estimatedScaleFactor);
  513. if (m_AdditionalLightsShadowSlices == null || m_AdditionalLightsShadowSlices.Length < totalShadowSlicesCount)
  514. m_AdditionalLightsShadowSlices = new ShadowSliceData[totalShadowSlicesCount];
  515. if (m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix == null ||
  516. (m_UseStructuredBuffer && (m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length < totalShadowSlicesCount))) // m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix can be resized when using SSBO to pass shadow data (no size limitation)
  517. m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[totalShadowSlicesCount];
  518. // initialize _AdditionalShadowParams
  519. for (int i = 0; i < maxAdditionalLightShadowParams; ++i)
  520. m_AdditionalLightIndexToShadowParams[i] = c_DefaultShadowParams;
  521. int validShadowCastingLightsCount = 0;
  522. bool supportsSoftShadows = renderingData.shadowData.supportsSoftShadows;
  523. int additionalLightCount = 0;
  524. for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length && m_ShadowSliceToAdditionalLightIndex.Count < totalShadowSlicesCount && additionalLightCount < maxAdditionalLightShadowParams; ++visibleLightIndex)
  525. {
  526. VisibleLight shadowLight = visibleLights[visibleLightIndex];
  527. // Skip main directional light as it is not packed into the shadow atlas
  528. if (visibleLightIndex == renderingData.lightData.mainLightIndex)
  529. {
  530. m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex] = -1;
  531. continue;
  532. }
  533. int additionalLightIndex = additionalLightCount++;
  534. m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex] = visibleLightIndex;
  535. m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex] = additionalLightIndex;
  536. LightType lightType = shadowLight.lightType;
  537. int perLightShadowSlicesCount = GetPunctualLightShadowSlicesCount(lightType);
  538. if ((m_ShadowSliceToAdditionalLightIndex.Count + perLightShadowSlicesCount) > totalShadowSlicesCount && IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
  539. {
  540. if (!m_IssuedMessageAboutShadowSlicesTooMany)
  541. {
  542. // This case can especially happen in Deferred, where there can be a high number of visibleLights
  543. Debug.Log($"There are too many shadowed additional punctual lights active at the same time, URP will not render all the shadows. To ensure all shadows are rendered, reduce the number of shadowed additional lights in the scene ; make sure they are not active at the same time ; or replace point lights by spot lights (spot lights use less shadow maps than point lights).");
  544. m_IssuedMessageAboutShadowSlicesTooMany = true; // Only output this once
  545. }
  546. break;
  547. }
  548. var originalLightIndex = renderingData.lightData.originalIndices[visibleLightIndex];
  549. int perLightFirstShadowSliceIndex = m_ShadowSliceToAdditionalLightIndex.Count; // shadowSliceIndex within the global array of all additional light shadow slices
  550. bool isValidShadowCastingLight = false;
  551. for (int perLightShadowSlice = 0; perLightShadowSlice < perLightShadowSlicesCount; ++perLightShadowSlice)
  552. {
  553. int globalShadowSliceIndex = m_ShadowSliceToAdditionalLightIndex.Count; // shadowSliceIndex within the global array of all additional light shadow slices
  554. bool lightRangeContainsShadowCasters = renderingData.cullResults.GetShadowCasterBounds(originalLightIndex, out var shadowCastersBounds);
  555. if (lightRangeContainsShadowCasters)
  556. {
  557. // We need to iterate the lights even though additional lights are disabled because
  558. // cullResults.GetShadowCasterBounds() does the fence sync for the shadow culling jobs.
  559. if (!renderingData.shadowData.supportsAdditionalLightShadows)
  560. continue;
  561. if (IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
  562. {
  563. if (m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] == -1)
  564. {
  565. // We could not find place in the shadow atlas for shadow maps of this light.
  566. // Skip it.
  567. }
  568. else if (lightType == LightType.Spot)
  569. {
  570. bool success = ShadowUtils.ExtractSpotLightMatrix(ref renderingData.cullResults,
  571. ref renderingData.shadowData,
  572. originalLightIndex,
  573. out var shadowTransform,
  574. out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].viewMatrix,
  575. out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].projectionMatrix,
  576. out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].splitData);
  577. if (success)
  578. {
  579. m_ShadowSliceToAdditionalLightIndex.Add(additionalLightIndex);
  580. m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Add(perLightShadowSlice);
  581. var light = shadowLight.light;
  582. float shadowStrength = light.shadowStrength;
  583. float softShadows = (supportsSoftShadows && light.shadows == LightShadows.Soft) ? 1.0f : 0.0f;
  584. Vector4 shadowParams = new Vector4(shadowStrength, softShadows, LightTypeIdentifierInShadowParams_Spot, perLightFirstShadowSliceIndex);
  585. m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = shadowTransform;
  586. m_AdditionalLightIndexToShadowParams[additionalLightIndex] = shadowParams;
  587. isValidShadowCastingLight = true;
  588. }
  589. }
  590. else if (lightType == LightType.Point)
  591. {
  592. var sliceResolution = m_SortedShadowResolutionRequests[m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex]].allocatedResolution;
  593. float fovBias = GetPointLightShadowFrustumFovBiasInDegrees(sliceResolution, (shadowLight.light.shadows == LightShadows.Soft));
  594. // Note: the same fovBias will also be used to compute ShadowUtils.GetShadowBias
  595. bool success = ShadowUtils.ExtractPointLightMatrix(ref renderingData.cullResults,
  596. ref renderingData.shadowData,
  597. originalLightIndex,
  598. (CubemapFace)perLightShadowSlice,
  599. fovBias,
  600. out var shadowTransform,
  601. out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].viewMatrix,
  602. out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].projectionMatrix,
  603. out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].splitData);
  604. if (success)
  605. {
  606. m_ShadowSliceToAdditionalLightIndex.Add(additionalLightIndex);
  607. m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Add(perLightShadowSlice);
  608. var light = shadowLight.light;
  609. float shadowStrength = light.shadowStrength;
  610. float softShadows = (supportsSoftShadows && light.shadows == LightShadows.Soft) ? 1.0f : 0.0f;
  611. Vector4 shadowParams = new Vector4(shadowStrength, softShadows, LightTypeIdentifierInShadowParams_Point, perLightFirstShadowSliceIndex);
  612. m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = shadowTransform;
  613. m_AdditionalLightIndexToShadowParams[additionalLightIndex] = shadowParams;
  614. isValidShadowCastingLight = true;
  615. }
  616. }
  617. }
  618. }
  619. }
  620. if (isValidShadowCastingLight)
  621. validShadowCastingLightsCount++;
  622. }
  623. // Lights that need to be rendered in the shadow map atlas
  624. if (validShadowCastingLightsCount == 0)
  625. return SetupForEmptyRendering(ref renderingData);
  626. int shadowCastingLightsBufferCount = m_ShadowSliceToAdditionalLightIndex.Count;
  627. // Trim shadow atlas dimensions if possible (to avoid allocating texture space that will not be used)
  628. int atlasMaxX = 0;
  629. int atlasMaxY = 0;
  630. for (int sortedShadowResolutionRequestIndex = 0; sortedShadowResolutionRequestIndex < totalShadowSlicesCount; ++sortedShadowResolutionRequestIndex)
  631. {
  632. var shadowResolutionRequest = m_SortedShadowResolutionRequests[sortedShadowResolutionRequestIndex];
  633. atlasMaxX = Mathf.Max(atlasMaxX, shadowResolutionRequest.offsetX + shadowResolutionRequest.allocatedResolution);
  634. atlasMaxY = Mathf.Max(atlasMaxY, shadowResolutionRequest.offsetY + shadowResolutionRequest.allocatedResolution);
  635. }
  636. // ...but make sure we still use power-of-two dimensions (might perform better on some hardware)
  637. renderTargetWidth = Mathf.NextPowerOfTwo(atlasMaxX);
  638. renderTargetHeight = Mathf.NextPowerOfTwo(atlasMaxY);
  639. float oneOverAtlasWidth = 1.0f / renderTargetWidth;
  640. float oneOverAtlasHeight = 1.0f / renderTargetHeight;
  641. Matrix4x4 sliceTransform;
  642. for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowCastingLightsBufferCount; ++globalShadowSliceIndex)
  643. {
  644. int additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
  645. // We can skip the slice if strength is zero.
  646. if (Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].x, 0.0f) || Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].w, -1.0f))
  647. continue;
  648. int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
  649. int sortedShadowResolutionRequestFirstSliceIndex = m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex];
  650. int perLightSliceIndex = m_GlobalShadowSliceIndexToPerLightShadowSliceIndex[globalShadowSliceIndex];
  651. var shadowResolutionRequest = m_SortedShadowResolutionRequests[sortedShadowResolutionRequestFirstSliceIndex + perLightSliceIndex];
  652. int sliceResolution = shadowResolutionRequest.allocatedResolution;
  653. sliceTransform = Matrix4x4.identity;
  654. sliceTransform.m00 = sliceResolution * oneOverAtlasWidth;
  655. sliceTransform.m11 = sliceResolution * oneOverAtlasHeight;
  656. m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetX = shadowResolutionRequest.offsetX;
  657. m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetY = shadowResolutionRequest.offsetY;
  658. m_AdditionalLightsShadowSlices[globalShadowSliceIndex].resolution = sliceResolution;
  659. sliceTransform.m03 = m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetX * oneOverAtlasWidth;
  660. sliceTransform.m13 = m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetY * oneOverAtlasHeight;
  661. // We bake scale and bias to each shadow map in the atlas in the matrix.
  662. // saves some instructions in shader.
  663. m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = sliceTransform * m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex];
  664. }
  665. m_AdditionalLightsShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(renderTargetWidth, renderTargetHeight, k_ShadowmapBufferBits);
  666. m_MaxShadowDistanceSq = renderingData.cameraData.maxShadowDistance * renderingData.cameraData.maxShadowDistance;
  667. m_CascadeBorder = renderingData.shadowData.mainLightShadowCascadeBorder;
  668. m_CreateEmptyShadowmap = false;
  669. useNativeRenderPass = true;
  670. return true;
  671. }
  672. bool SetupForEmptyRendering(ref RenderingData renderingData)
  673. {
  674. if (!renderingData.cameraData.renderer.stripShadowsOffVariants)
  675. return false;
  676. m_AdditionalLightsShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(1, 1, k_ShadowmapBufferBits);
  677. m_CreateEmptyShadowmap = true;
  678. useNativeRenderPass = false;
  679. // initialize _AdditionalShadowParams
  680. for (int i = 0; i < m_AdditionalLightIndexToShadowParams.Length; ++i)
  681. m_AdditionalLightIndexToShadowParams[i] = c_DefaultShadowParams;
  682. return true;
  683. }
  684. public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
  685. {
  686. ConfigureTarget(new RenderTargetIdentifier(m_AdditionalLightsShadowmapTexture), m_AdditionalLightsShadowmapTexture.depthStencilFormat, renderTargetWidth, renderTargetHeight, 1, true);
  687. ConfigureClear(ClearFlag.All, Color.black);
  688. }
  689. /// <inheritdoc/>
  690. public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
  691. {
  692. if (m_CreateEmptyShadowmap)
  693. {
  694. SetEmptyAdditionalShadowmapAtlas(ref context);
  695. return;
  696. }
  697. if (renderingData.shadowData.supportsAdditionalLightShadows)
  698. RenderAdditionalShadowmapAtlas(ref context, ref renderingData.cullResults, ref renderingData.lightData, ref renderingData.shadowData);
  699. }
  700. public override void OnCameraCleanup(CommandBuffer cmd)
  701. {
  702. if (cmd == null)
  703. throw new ArgumentNullException("cmd");
  704. if (m_AdditionalLightsShadowmapTexture)
  705. {
  706. RenderTexture.ReleaseTemporary(m_AdditionalLightsShadowmapTexture);
  707. m_AdditionalLightsShadowmapTexture = null;
  708. }
  709. }
  710. // Get the "additional light index" (used to index arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...) from the "global" visible light index
  711. // Function called by Deferred Renderer
  712. public int GetShadowLightIndexFromLightIndex(int visibleLightIndex)
  713. {
  714. if (visibleLightIndex < 0 || visibleLightIndex >= m_VisibleLightIndexToAdditionalLightIndex.Length)
  715. return -1;
  716. return m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex];
  717. }
  718. void Clear()
  719. {
  720. m_ShadowSliceToAdditionalLightIndex.Clear();
  721. m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Clear();
  722. m_AdditionalLightsShadowmapTexture = null;
  723. }
  724. void SetEmptyAdditionalShadowmapAtlas(ref ScriptableRenderContext context)
  725. {
  726. CommandBuffer cmd = CommandBufferPool.Get();
  727. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightShadows, true);
  728. cmd.SetGlobalTexture(m_AdditionalLightsShadowmap.id, m_AdditionalLightsShadowmapTexture);
  729. if (RenderingUtils.useStructuredBuffer)
  730. {
  731. var shadowParamsBuffer = ShaderData.instance.GetAdditionalLightShadowParamsStructuredBuffer(m_AdditionalLightIndexToShadowParams.Length);
  732. shadowParamsBuffer.SetData(m_AdditionalLightIndexToShadowParams);
  733. cmd.SetGlobalBuffer(m_AdditionalShadowParams_SSBO, shadowParamsBuffer);
  734. }
  735. else
  736. {
  737. cmd.SetGlobalVectorArray(AdditionalShadowsConstantBuffer._AdditionalShadowParams, m_AdditionalLightIndexToShadowParams);
  738. }
  739. context.ExecuteCommandBuffer(cmd);
  740. CommandBufferPool.Release(cmd);
  741. }
  742. void RenderAdditionalShadowmapAtlas(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData)
  743. {
  744. NativeArray<VisibleLight> visibleLights = lightData.visibleLights;
  745. bool additionalLightHasSoftShadows = false;
  746. // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
  747. // Currently there's an issue which results in mismatched markers.
  748. CommandBuffer cmd = CommandBufferPool.Get();
  749. using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.AdditionalLightsShadow)))
  750. {
  751. bool anyShadowSliceRenderer = false;
  752. int shadowSlicesCount = m_ShadowSliceToAdditionalLightIndex.Count;
  753. for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowSlicesCount; ++globalShadowSliceIndex)
  754. {
  755. int additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
  756. // we do the shadow strength check here again here because we might have zero strength for non-shadow-casting lights.
  757. // In that case we need the shadow data buffer but we can skip rendering them to shadowmap.
  758. if (Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].x, 0.0f) || Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].w, -1.0f))
  759. continue;
  760. int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
  761. var originalLightIndex = lightData.originalIndices[visibleLightIndex];
  762. VisibleLight shadowLight = visibleLights[visibleLightIndex];
  763. ShadowSliceData shadowSliceData = m_AdditionalLightsShadowSlices[globalShadowSliceIndex];
  764. var settings = new ShadowDrawingSettings(cullResults, originalLightIndex);
  765. settings.useRenderingLayerMaskTest = UniversalRenderPipeline.asset.supportsLightLayers;
  766. settings.splitData = shadowSliceData.splitData;
  767. Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, visibleLightIndex,
  768. ref shadowData, shadowSliceData.projectionMatrix, shadowSliceData.resolution);
  769. ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
  770. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, true);
  771. ShadowUtils.RenderShadowSlice(cmd, ref context, ref shadowSliceData, ref settings);
  772. additionalLightHasSoftShadows |= shadowLight.light.shadows == LightShadows.Soft;
  773. anyShadowSliceRenderer = true;
  774. }
  775. // We share soft shadow settings for main light and additional lights to save keywords.
  776. // So we check here if pipeline supports soft shadows and either main light or any additional light has soft shadows
  777. // to enable the keyword.
  778. // TODO: In PC and Consoles we can upload shadow data per light and branch on shader. That will be more likely way faster.
  779. bool mainLightHasSoftShadows = shadowData.supportsMainLightShadows &&
  780. lightData.mainLightIndex != -1 &&
  781. visibleLights[lightData.mainLightIndex].light.shadows ==
  782. LightShadows.Soft;
  783. bool softShadows = shadowData.supportsSoftShadows &&
  784. (mainLightHasSoftShadows || additionalLightHasSoftShadows);
  785. shadowData.isKeywordAdditionalLightShadowsEnabled = anyShadowSliceRenderer;
  786. shadowData.isKeywordSoftShadowsEnabled = softShadows;
  787. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightShadows, shadowData.isKeywordAdditionalLightShadowsEnabled);
  788. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, shadowData.isKeywordSoftShadowsEnabled);
  789. if (anyShadowSliceRenderer)
  790. SetupAdditionalLightsShadowReceiverConstants(cmd, ref shadowData, softShadows);
  791. }
  792. context.ExecuteCommandBuffer(cmd);
  793. CommandBufferPool.Release(cmd);
  794. }
  795. // Set constant buffer data that will be used during the lighting/shadowing pass
  796. void SetupAdditionalLightsShadowReceiverConstants(CommandBuffer cmd, ref ShadowData shadowData, bool softShadows)
  797. {
  798. float invShadowAtlasWidth = 1.0f / shadowData.additionalLightsShadowmapWidth;
  799. float invShadowAtlasHeight = 1.0f / shadowData.additionalLightsShadowmapHeight;
  800. float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
  801. float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
  802. cmd.SetGlobalTexture(m_AdditionalLightsShadowmap.id, m_AdditionalLightsShadowmapTexture);
  803. if (m_UseStructuredBuffer)
  804. {
  805. // per-light data
  806. var shadowParamsBuffer = ShaderData.instance.GetAdditionalLightShadowParamsStructuredBuffer(m_AdditionalLightIndexToShadowParams.Length);
  807. shadowParamsBuffer.SetData(m_AdditionalLightIndexToShadowParams);
  808. cmd.SetGlobalBuffer(m_AdditionalShadowParams_SSBO, shadowParamsBuffer);
  809. // per-shadow-slice data
  810. var shadowSliceMatricesBuffer = ShaderData.instance.GetAdditionalLightShadowSliceMatricesStructuredBuffer(m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length);
  811. shadowSliceMatricesBuffer.SetData(m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix);
  812. cmd.SetGlobalBuffer(m_AdditionalLightsWorldToShadow_SSBO, shadowSliceMatricesBuffer);
  813. }
  814. else
  815. {
  816. cmd.SetGlobalVectorArray(AdditionalShadowsConstantBuffer._AdditionalShadowParams, m_AdditionalLightIndexToShadowParams); // per-light data
  817. cmd.SetGlobalMatrixArray(AdditionalShadowsConstantBuffer._AdditionalLightsWorldToShadow, m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix); // per-shadow-slice data
  818. }
  819. ShadowUtils.GetScaleAndBiasForLinearDistanceFade(m_MaxShadowDistanceSq, m_CascadeBorder, out float shadowFadeScale, out float shadowFadeBias);
  820. cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowFadeParams, new Vector4(shadowFadeScale, shadowFadeBias, 0, 0));
  821. if (softShadows)
  822. {
  823. cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset0,
  824. new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
  825. cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset1,
  826. new Vector4(invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
  827. cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset2,
  828. new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
  829. cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset3,
  830. new Vector4(invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
  831. // Currently only used when !SHADER_API_MOBILE but risky to not set them as it's generic
  832. // enough so custom shaders might use it.
  833. cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowmapSize, new Vector4(invShadowAtlasWidth, invShadowAtlasHeight,
  834. shadowData.additionalLightsShadowmapWidth, shadowData.additionalLightsShadowmapHeight));
  835. }
  836. }
  837. bool IsValidShadowCastingLight(ref LightData lightData, int i)
  838. {
  839. if (i == lightData.mainLightIndex)
  840. return false;
  841. VisibleLight shadowLight = lightData.visibleLights[i];
  842. // Directional and light shadows are not supported in the shadow map atlas
  843. if (shadowLight.lightType == LightType.Directional)
  844. return false;
  845. Light light = shadowLight.light;
  846. return light != null && light.shadows != LightShadows.None && !Mathf.Approximately(light.shadowStrength, 0.0f);
  847. }
  848. }
  849. }