Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using Unity.Collections;
  6. using Unity.Jobs;
  7. using Unity.Mathematics;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using UnityEngine.Experimental.Rendering;
  10. using UnityEngine.Rendering.RenderGraphModule;
  11. namespace UnityEngine.Rendering.Universal.Internal
  12. {
  13. /// <summary>
  14. /// Computes and submits lighting data to the GPU.
  15. /// </summary>
  16. public class ForwardLights
  17. {
  18. static class LightConstantBuffer
  19. {
  20. public static int _MainLightPosition; // DeferredLights.LightConstantBuffer also refers to the same ShaderPropertyID - TODO: move this definition to a common location shared by other UniversalRP classes
  21. public static int _MainLightColor; // DeferredLights.LightConstantBuffer also refers to the same ShaderPropertyID - TODO: move this definition to a common location shared by other UniversalRP classes
  22. public static int _MainLightOcclusionProbesChannel; // Deferred?
  23. public static int _MainLightLayerMask;
  24. public static int _AdditionalLightsCount;
  25. public static int _AdditionalLightsPosition;
  26. public static int _AdditionalLightsColor;
  27. public static int _AdditionalLightsAttenuation;
  28. public static int _AdditionalLightsSpotDir;
  29. public static int _AdditionalLightOcclusionProbeChannel;
  30. public static int _AdditionalLightsLayerMasks;
  31. }
  32. int m_AdditionalLightsBufferId;
  33. int m_AdditionalLightsIndicesId;
  34. const string k_SetupLightConstants = "Setup Light Constants";
  35. private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(k_SetupLightConstants);
  36. private static readonly ProfilingSampler m_ProfilingSamplerFPSetup = new ProfilingSampler("Forward+ Setup");
  37. private static readonly ProfilingSampler m_ProfilingSamplerFPComplete = new ProfilingSampler("Forward+ Complete");
  38. private static readonly ProfilingSampler m_ProfilingSamplerFPUpload = new ProfilingSampler("Forward+ Upload");
  39. MixedLightingSetup m_MixedLightingSetup;
  40. Vector4[] m_AdditionalLightPositions;
  41. Vector4[] m_AdditionalLightColors;
  42. Vector4[] m_AdditionalLightAttenuations;
  43. Vector4[] m_AdditionalLightSpotDirections;
  44. Vector4[] m_AdditionalLightOcclusionProbeChannels;
  45. float[] m_AdditionalLightsLayerMasks; // Unity has no support for binding uint arrays. We will use asuint() in the shader instead.
  46. bool m_UseStructuredBuffer;
  47. bool m_UseForwardPlus;
  48. int m_DirectionalLightCount;
  49. int m_ActualTileWidth;
  50. int2 m_TileResolution;
  51. JobHandle m_CullingHandle;
  52. NativeArray<uint> m_ZBins;
  53. GraphicsBuffer m_ZBinsBuffer;
  54. NativeArray<uint> m_TileMasks;
  55. GraphicsBuffer m_TileMasksBuffer;
  56. LightCookieManager m_LightCookieManager;
  57. ReflectionProbeManager m_ReflectionProbeManager;
  58. int m_WordsPerTile;
  59. float m_ZBinScale;
  60. float m_ZBinOffset;
  61. int m_LightCount;
  62. int m_BinCount;
  63. internal struct InitParams
  64. {
  65. public LightCookieManager lightCookieManager;
  66. public bool forwardPlus;
  67. static internal InitParams Create()
  68. {
  69. InitParams p;
  70. {
  71. var settings = LightCookieManager.Settings.Create();
  72. var asset = UniversalRenderPipeline.asset;
  73. if (asset)
  74. {
  75. settings.atlas.format = asset.additionalLightsCookieFormat;
  76. settings.atlas.resolution = asset.additionalLightsCookieResolution;
  77. }
  78. p.lightCookieManager = new LightCookieManager(ref settings);
  79. p.forwardPlus = false;
  80. }
  81. return p;
  82. }
  83. }
  84. /// <summary>
  85. /// Creates a new <c>ForwardLights</c> instance.
  86. /// </summary>
  87. public ForwardLights() : this(InitParams.Create()) { }
  88. internal ForwardLights(InitParams initParams)
  89. {
  90. m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
  91. m_UseForwardPlus = initParams.forwardPlus;
  92. LightConstantBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition");
  93. LightConstantBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor");
  94. LightConstantBuffer._MainLightOcclusionProbesChannel = Shader.PropertyToID("_MainLightOcclusionProbes");
  95. LightConstantBuffer._MainLightLayerMask = Shader.PropertyToID("_MainLightLayerMask");
  96. LightConstantBuffer._AdditionalLightsCount = Shader.PropertyToID("_AdditionalLightsCount");
  97. if (m_UseStructuredBuffer)
  98. {
  99. m_AdditionalLightsBufferId = Shader.PropertyToID("_AdditionalLightsBuffer");
  100. m_AdditionalLightsIndicesId = Shader.PropertyToID("_AdditionalLightsIndices");
  101. }
  102. else
  103. {
  104. LightConstantBuffer._AdditionalLightsPosition = Shader.PropertyToID("_AdditionalLightsPosition");
  105. LightConstantBuffer._AdditionalLightsColor = Shader.PropertyToID("_AdditionalLightsColor");
  106. LightConstantBuffer._AdditionalLightsAttenuation = Shader.PropertyToID("_AdditionalLightsAttenuation");
  107. LightConstantBuffer._AdditionalLightsSpotDir = Shader.PropertyToID("_AdditionalLightsSpotDir");
  108. LightConstantBuffer._AdditionalLightOcclusionProbeChannel = Shader.PropertyToID("_AdditionalLightsOcclusionProbes");
  109. LightConstantBuffer._AdditionalLightsLayerMasks = Shader.PropertyToID("_AdditionalLightsLayerMasks");
  110. int maxLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
  111. m_AdditionalLightPositions = new Vector4[maxLights];
  112. m_AdditionalLightColors = new Vector4[maxLights];
  113. m_AdditionalLightAttenuations = new Vector4[maxLights];
  114. m_AdditionalLightSpotDirections = new Vector4[maxLights];
  115. m_AdditionalLightOcclusionProbeChannels = new Vector4[maxLights];
  116. m_AdditionalLightsLayerMasks = new float[maxLights];
  117. }
  118. if (m_UseForwardPlus)
  119. {
  120. CreateForwardPlusBuffers();
  121. m_ReflectionProbeManager = ReflectionProbeManager.Create();
  122. }
  123. m_LightCookieManager = initParams.lightCookieManager;
  124. }
  125. void CreateForwardPlusBuffers()
  126. {
  127. m_ZBins = new NativeArray<uint>(UniversalRenderPipeline.maxZBinWords, Allocator.Persistent);
  128. m_ZBinsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Constant, UniversalRenderPipeline.maxZBinWords / 4, UnsafeUtility.SizeOf<float4>());
  129. m_ZBinsBuffer.name = "URP Z-Bin Buffer";
  130. m_TileMasks = new NativeArray<uint>(UniversalRenderPipeline.maxTileWords, Allocator.Persistent);
  131. m_TileMasksBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Constant, UniversalRenderPipeline.maxTileWords / 4, UnsafeUtility.SizeOf<float4>());
  132. m_TileMasksBuffer.name = "URP Tile Buffer";
  133. }
  134. internal ReflectionProbeManager reflectionProbeManager => m_ReflectionProbeManager;
  135. static int AlignByteCount(int count, int align) => align * ((count + align - 1) / align);
  136. // Calculate view planes and viewToViewportScaleBias. This handles projection center in case the projection is off-centered
  137. void GetViewParams(Camera camera, float4x4 viewToClip, out float viewPlaneBot, out float viewPlaneTop, out float4 viewToViewportScaleBias)
  138. {
  139. // We want to calculate `fovHalfHeight = tan(fov / 2)`
  140. // `projection[1][1]` contains `1 / tan(fov / 2)`
  141. var viewPlaneHalfSizeInv = math.float2(viewToClip[0][0], viewToClip[1][1]);
  142. var viewPlaneHalfSize = math.rcp(viewPlaneHalfSizeInv);
  143. var centerClipSpace = camera.orthographic ? -math.float2(viewToClip[3][0], viewToClip[3][1]): math.float2(viewToClip[2][0], viewToClip[2][1]);
  144. viewPlaneBot = centerClipSpace.y * viewPlaneHalfSize.y - viewPlaneHalfSize.y;
  145. viewPlaneTop = centerClipSpace.y * viewPlaneHalfSize.y + viewPlaneHalfSize.y;
  146. viewToViewportScaleBias = math.float4(
  147. viewPlaneHalfSizeInv * 0.5f,
  148. -centerClipSpace * 0.5f + 0.5f
  149. );
  150. }
  151. internal void PreSetup(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData)
  152. {
  153. if (m_UseForwardPlus)
  154. {
  155. using var _ = new ProfilingScope(m_ProfilingSamplerFPSetup);
  156. if (!m_CullingHandle.IsCompleted)
  157. {
  158. throw new InvalidOperationException("Forward+ jobs have not completed yet.");
  159. }
  160. if (m_TileMasks.Length != UniversalRenderPipeline.maxTileWords)
  161. {
  162. m_ZBins.Dispose();
  163. m_ZBinsBuffer.Dispose();
  164. m_TileMasks.Dispose();
  165. m_TileMasksBuffer.Dispose();
  166. CreateForwardPlusBuffers();
  167. }
  168. else
  169. {
  170. unsafe
  171. {
  172. UnsafeUtility.MemClear(m_ZBins.GetUnsafePtr(), m_ZBins.Length * sizeof(uint));
  173. UnsafeUtility.MemClear(m_TileMasks.GetUnsafePtr(), m_TileMasks.Length * sizeof(uint));
  174. }
  175. }
  176. var camera = cameraData.camera;
  177. var screenResolution = math.int2(cameraData.pixelWidth, cameraData.pixelHeight);
  178. #if ENABLE_VR && ENABLE_XR_MODULE
  179. var viewCount = cameraData.xr.enabled && cameraData.xr.singlePassEnabled ? 2 : 1;
  180. #else
  181. var viewCount = 1;
  182. #endif
  183. m_LightCount = lightData.visibleLights.Length;
  184. var lightOffset = 0;
  185. while (lightOffset < m_LightCount && lightData.visibleLights[lightOffset].lightType == LightType.Directional)
  186. {
  187. lightOffset++;
  188. }
  189. m_LightCount -= lightOffset;
  190. m_DirectionalLightCount = lightOffset;
  191. if (lightData.mainLightIndex != -1 && m_DirectionalLightCount != 0) m_DirectionalLightCount -= 1;
  192. var visibleLights = lightData.visibleLights.GetSubArray(lightOffset, m_LightCount);
  193. var reflectionProbes = renderingData.cullResults.visibleReflectionProbes;
  194. var reflectionProbeCount = math.min(reflectionProbes.Length, UniversalRenderPipeline.maxVisibleReflectionProbes);
  195. var itemsPerTile = visibleLights.Length + reflectionProbeCount;
  196. m_WordsPerTile = (itemsPerTile + 31) / 32;
  197. m_ActualTileWidth = 8 >> 1;
  198. do
  199. {
  200. m_ActualTileWidth <<= 1;
  201. m_TileResolution = (screenResolution + m_ActualTileWidth - 1) / m_ActualTileWidth;
  202. }
  203. while ((m_TileResolution.x * m_TileResolution.y * m_WordsPerTile * viewCount) > UniversalRenderPipeline.maxTileWords);
  204. if (!camera.orthographic)
  205. {
  206. // Use to calculate binIndex = log2(z) * zBinScale + zBinOffset
  207. m_ZBinScale = (UniversalRenderPipeline.maxZBinWords / viewCount) / ((math.log2(camera.farClipPlane) - math.log2(camera.nearClipPlane)) * (2 + m_WordsPerTile));
  208. m_ZBinOffset = -math.log2(camera.nearClipPlane) * m_ZBinScale;
  209. m_BinCount = (int)(math.log2(camera.farClipPlane) * m_ZBinScale + m_ZBinOffset);
  210. }
  211. else
  212. {
  213. // Use to calculate binIndex = z * zBinScale + zBinOffset
  214. m_ZBinScale = (UniversalRenderPipeline.maxZBinWords / viewCount) / ((camera.farClipPlane - camera.nearClipPlane) * (2 + m_WordsPerTile));
  215. m_ZBinOffset = -camera.nearClipPlane * m_ZBinScale;
  216. m_BinCount = (int)(camera.farClipPlane * m_ZBinScale + m_ZBinOffset);
  217. }
  218. var worldToViews = new Fixed2<float4x4>(cameraData.GetViewMatrix(0), cameraData.GetViewMatrix(math.min(1, viewCount - 1)));
  219. var viewToClips = new Fixed2<float4x4>(cameraData.GetProjectionMatrix(0), cameraData.GetProjectionMatrix(math.min(1, viewCount - 1)));
  220. // Should probe come after otherProbe?
  221. static bool IsProbeGreater(VisibleReflectionProbe probe, VisibleReflectionProbe otherProbe)
  222. {
  223. return probe.importance < otherProbe.importance ||
  224. (probe.importance == otherProbe.importance && probe.bounds.extents.sqrMagnitude > otherProbe.bounds.extents.sqrMagnitude);
  225. }
  226. for (var i = 1; i < reflectionProbeCount; i++)
  227. {
  228. var probe = reflectionProbes[i];
  229. var j = i - 1;
  230. while (j >= 0 && IsProbeGreater(reflectionProbes[j], probe))
  231. {
  232. reflectionProbes[j + 1] = reflectionProbes[j];
  233. j--;
  234. }
  235. reflectionProbes[j + 1] = probe;
  236. }
  237. var minMaxZs = new NativeArray<float2>(itemsPerTile * viewCount, Allocator.TempJob);
  238. var lightMinMaxZJob = new LightMinMaxZJob
  239. {
  240. worldToViews = worldToViews,
  241. lights = visibleLights,
  242. minMaxZs = minMaxZs.GetSubArray(0, m_LightCount * viewCount)
  243. };
  244. // Innerloop batch count of 32 is not special, just a handwavy amount to not have too much scheduling overhead nor too little parallelism.
  245. var lightMinMaxZHandle = lightMinMaxZJob.ScheduleParallel(m_LightCount * viewCount, 32, new JobHandle());
  246. var reflectionProbeMinMaxZJob = new ReflectionProbeMinMaxZJob
  247. {
  248. worldToViews = worldToViews,
  249. reflectionProbes = reflectionProbes,
  250. minMaxZs = minMaxZs.GetSubArray(m_LightCount * viewCount, reflectionProbeCount * viewCount)
  251. };
  252. var reflectionProbeMinMaxZHandle = reflectionProbeMinMaxZJob.ScheduleParallel(reflectionProbeCount * viewCount, 32, lightMinMaxZHandle);
  253. var zBinningBatchCount = (m_BinCount + ZBinningJob.batchSize - 1) / ZBinningJob.batchSize;
  254. var zBinningJob = new ZBinningJob
  255. {
  256. bins = m_ZBins,
  257. minMaxZs = minMaxZs,
  258. zBinScale = m_ZBinScale,
  259. zBinOffset = m_ZBinOffset,
  260. binCount = m_BinCount,
  261. wordsPerTile = m_WordsPerTile,
  262. lightCount = m_LightCount,
  263. reflectionProbeCount = reflectionProbeCount,
  264. batchCount = zBinningBatchCount,
  265. viewCount = viewCount,
  266. isOrthographic = camera.orthographic
  267. };
  268. var zBinningHandle = zBinningJob.ScheduleParallel(zBinningBatchCount * viewCount, 1, reflectionProbeMinMaxZHandle);
  269. reflectionProbeMinMaxZHandle.Complete();
  270. GetViewParams(camera, viewToClips[0], out float viewPlaneBottom0, out float viewPlaneTop0, out float4 viewToViewportScaleBias0);
  271. GetViewParams(camera, viewToClips[1], out float viewPlaneBottom1, out float viewPlaneTop1, out float4 viewToViewportScaleBias1);
  272. // Each light needs 1 range for Y, and a range per row. Align to 128-bytes to avoid false sharing.
  273. var rangesPerItem = AlignByteCount((1 + m_TileResolution.y) * UnsafeUtility.SizeOf<InclusiveRange>(), 128) / UnsafeUtility.SizeOf<InclusiveRange>();
  274. var tileRanges = new NativeArray<InclusiveRange>(rangesPerItem * itemsPerTile * viewCount, Allocator.TempJob);
  275. var tilingJob = new TilingJob
  276. {
  277. lights = visibleLights,
  278. reflectionProbes = reflectionProbes,
  279. tileRanges = tileRanges,
  280. itemsPerTile = itemsPerTile,
  281. rangesPerItem = rangesPerItem,
  282. worldToViews = worldToViews,
  283. tileScale = (float2)screenResolution / m_ActualTileWidth,
  284. tileScaleInv = m_ActualTileWidth / (float2)screenResolution,
  285. viewPlaneBottoms = new Fixed2<float>(viewPlaneBottom0, viewPlaneBottom1),
  286. viewPlaneTops = new Fixed2<float>(viewPlaneTop0, viewPlaneTop1),
  287. viewToViewportScaleBiases = new Fixed2<float4>(viewToViewportScaleBias0, viewToViewportScaleBias1),
  288. tileCount = m_TileResolution,
  289. near = camera.nearClipPlane,
  290. isOrthographic = camera.orthographic
  291. };
  292. var tileRangeHandle = tilingJob.ScheduleParallel(itemsPerTile * viewCount, 1, reflectionProbeMinMaxZHandle);
  293. var expansionJob = new TileRangeExpansionJob
  294. {
  295. tileRanges = tileRanges,
  296. tileMasks = m_TileMasks,
  297. rangesPerItem = rangesPerItem,
  298. itemsPerTile = itemsPerTile,
  299. wordsPerTile = m_WordsPerTile,
  300. tileResolution = m_TileResolution,
  301. };
  302. var tilingHandle = expansionJob.ScheduleParallel(m_TileResolution.y * viewCount, 1, tileRangeHandle);
  303. m_CullingHandle = JobHandle.CombineDependencies(
  304. minMaxZs.Dispose(zBinningHandle),
  305. tileRanges.Dispose(tilingHandle));
  306. JobHandle.ScheduleBatchedJobs();
  307. }
  308. }
  309. /// <summary>
  310. /// Sets up the keywords and data for forward lighting.
  311. /// </summary>
  312. /// <param name="context"></param>
  313. /// <param name="renderingData"></param>
  314. public void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
  315. {
  316. ContextContainer frameData = renderingData.frameData;
  317. UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
  318. UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
  319. UniversalLightData lightData = frameData.Get<UniversalLightData>();
  320. SetupLights(CommandBufferHelpers.GetUnsafeCommandBuffer(renderingData.commandBuffer), universalRenderingData, cameraData, lightData);
  321. }
  322. static ProfilingSampler s_SetupForwardLights = new ProfilingSampler("Setup Forward Lights");
  323. private class SetupLightPassData
  324. {
  325. internal UniversalRenderingData renderingData;
  326. internal UniversalCameraData cameraData;
  327. internal UniversalLightData lightData;
  328. internal ForwardLights forwardLights;
  329. };
  330. /// <summary>
  331. /// Sets up the ForwardLight data for RenderGraph execution
  332. /// </summary>
  333. internal void SetupRenderGraphLights(RenderGraph renderGraph, UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData)
  334. {
  335. using (var builder = renderGraph.AddUnsafePass<SetupLightPassData>(s_SetupForwardLights.name, out var passData,
  336. s_SetupForwardLights))
  337. {
  338. passData.renderingData = renderingData;
  339. passData.cameraData = cameraData;
  340. passData.lightData = lightData;
  341. passData.forwardLights = this;
  342. builder.AllowPassCulling(false);
  343. builder.SetRenderFunc((SetupLightPassData data, UnsafeGraphContext rgContext) =>
  344. {
  345. data.forwardLights.SetupLights(rgContext.cmd, data.renderingData, data.cameraData, data.lightData);
  346. });
  347. }
  348. }
  349. internal void SetupLights(UnsafeCommandBuffer cmd, UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData)
  350. {
  351. int additionalLightsCount = lightData.additionalLightsCount;
  352. bool additionalLightsPerVertex = lightData.shadeAdditionalLightsPerVertex;
  353. using (new ProfilingScope(m_ProfilingSampler))
  354. {
  355. if (m_UseForwardPlus)
  356. {
  357. m_ReflectionProbeManager.UpdateGpuData(CommandBufferHelpers.GetNativeCommandBuffer(cmd), ref renderingData.cullResults);
  358. using (new ProfilingScope(m_ProfilingSamplerFPComplete))
  359. {
  360. m_CullingHandle.Complete();
  361. }
  362. using (new ProfilingScope(m_ProfilingSamplerFPUpload))
  363. {
  364. m_ZBinsBuffer.SetData(m_ZBins.Reinterpret<float4>(UnsafeUtility.SizeOf<uint>()));
  365. m_TileMasksBuffer.SetData(m_TileMasks.Reinterpret<float4>(UnsafeUtility.SizeOf<uint>()));
  366. cmd.SetGlobalConstantBuffer(m_ZBinsBuffer, "urp_ZBinBuffer", 0, UniversalRenderPipeline.maxZBinWords * 4);
  367. cmd.SetGlobalConstantBuffer(m_TileMasksBuffer, "urp_TileBuffer", 0, UniversalRenderPipeline.maxTileWords * 4);
  368. }
  369. cmd.SetGlobalVector("_FPParams0", math.float4(m_ZBinScale, m_ZBinOffset, m_LightCount, m_DirectionalLightCount));
  370. cmd.SetGlobalVector("_FPParams1", math.float4(cameraData.pixelRect.size / m_ActualTileWidth, m_TileResolution.x, m_WordsPerTile));
  371. cmd.SetGlobalVector("_FPParams2", math.float4(m_BinCount, m_TileResolution.x * m_TileResolution.y, 0, 0));
  372. }
  373. SetupShaderLightConstants(cmd, ref renderingData.cullResults, lightData);
  374. bool lightCountCheck = (cameraData.renderer.stripAdditionalLightOffVariants && lightData.supportsAdditionalLights) || additionalLightsCount > 0;
  375. cmd.SetKeyword(ShaderGlobalKeywords.AdditionalLightsVertex, lightCountCheck && additionalLightsPerVertex && !m_UseForwardPlus);
  376. cmd.SetKeyword(ShaderGlobalKeywords.AdditionalLightsPixel, lightCountCheck && !additionalLightsPerVertex && !m_UseForwardPlus);
  377. cmd.SetKeyword(ShaderGlobalKeywords.ForwardPlus, m_UseForwardPlus);
  378. bool isShadowMask = lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.ShadowMask;
  379. bool isShadowMaskAlways = isShadowMask && QualitySettings.shadowmaskMode == ShadowmaskMode.Shadowmask;
  380. bool isSubtractive = lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.Subtractive;
  381. cmd.SetKeyword(ShaderGlobalKeywords.LightmapShadowMixing, isSubtractive || isShadowMaskAlways);
  382. cmd.SetKeyword(ShaderGlobalKeywords.ShadowsShadowMask, isShadowMask);
  383. cmd.SetKeyword(ShaderGlobalKeywords.MixedLightingSubtractive, isSubtractive); // Backward compatibility
  384. cmd.SetKeyword(ShaderGlobalKeywords.ReflectionProbeBlending, lightData.reflectionProbeBlending);
  385. cmd.SetKeyword(ShaderGlobalKeywords.ReflectionProbeBoxProjection, lightData.reflectionProbeBoxProjection);
  386. var asset = UniversalRenderPipeline.asset;
  387. bool apvIsEnabled = asset != null && asset.lightProbeSystem == LightProbeSystem.ProbeVolumes;
  388. ProbeVolumeSHBands probeVolumeSHBands = asset.probeVolumeSHBands;
  389. cmd.SetKeyword(ShaderGlobalKeywords.ProbeVolumeL1, apvIsEnabled && probeVolumeSHBands == ProbeVolumeSHBands.SphericalHarmonicsL1);
  390. cmd.SetKeyword(ShaderGlobalKeywords.ProbeVolumeL2, apvIsEnabled && probeVolumeSHBands == ProbeVolumeSHBands.SphericalHarmonicsL2);
  391. // TODO: If we can robustly detect LIGHTMAP_ON, we can skip SH logic.
  392. var shMode = PlatformAutoDetect.ShAutoDetect(asset.shEvalMode);
  393. cmd.SetKeyword(ShaderGlobalKeywords.EVALUATE_SH_MIXED, shMode == ShEvalMode.Mixed);
  394. cmd.SetKeyword(ShaderGlobalKeywords.EVALUATE_SH_VERTEX, shMode == ShEvalMode.PerVertex);
  395. var stack = VolumeManager.instance.stack;
  396. bool enableProbeVolumes = ProbeReferenceVolume.instance.UpdateShaderVariablesProbeVolumes(
  397. CommandBufferHelpers.GetNativeCommandBuffer(cmd),
  398. stack.GetComponent<ProbeVolumesOptions>(),
  399. cameraData.IsTemporalAAEnabled() ? Time.frameCount : 0,
  400. lightData.supportsLightLayers);
  401. cmd.SetGlobalInt("_EnableProbeVolumes", enableProbeVolumes ? 1 : 0);
  402. cmd.SetKeyword(ShaderGlobalKeywords.LightLayers, lightData.supportsLightLayers && !CoreUtils.IsSceneLightingDisabled(cameraData.camera));
  403. if (m_LightCookieManager != null)
  404. {
  405. m_LightCookieManager.Setup(CommandBufferHelpers.GetNativeCommandBuffer(cmd), lightData);
  406. }
  407. else
  408. {
  409. cmd.SetKeyword(ShaderGlobalKeywords.LightCookies, false);
  410. }
  411. }
  412. }
  413. internal void Cleanup()
  414. {
  415. if (m_UseForwardPlus)
  416. {
  417. m_CullingHandle.Complete();
  418. m_ZBins.Dispose();
  419. m_TileMasks.Dispose();
  420. m_ZBinsBuffer.Dispose();
  421. m_ZBinsBuffer = null;
  422. m_TileMasksBuffer.Dispose();
  423. m_TileMasksBuffer = null;
  424. m_ReflectionProbeManager.Dispose();
  425. }
  426. }
  427. void InitializeLightConstants(NativeArray<VisibleLight> lights, int lightIndex, bool supportsLightLayers, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightAttenuation, out Vector4 lightSpotDir, out Vector4 lightOcclusionProbeChannel, out uint lightLayerMask, out bool isSubtractive)
  428. {
  429. UniversalRenderPipeline.InitializeLightConstants_Common(lights, lightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionProbeChannel);
  430. lightLayerMask = 0;
  431. isSubtractive = false;
  432. // When no lights are visible, main light will be set to -1.
  433. // In this case we initialize it to default values and return
  434. if (lightIndex < 0)
  435. return;
  436. ref VisibleLight lightData = ref lights.UnsafeElementAtMutable(lightIndex);
  437. Light light = lightData.light;
  438. var lightBakingOutput = light.bakingOutput;
  439. isSubtractive = lightBakingOutput.isBaked && lightBakingOutput.lightmapBakeType == LightmapBakeType.Mixed && lightBakingOutput.mixedLightingMode == MixedLightingMode.Subtractive;
  440. if (light == null)
  441. return;
  442. if (lightBakingOutput.lightmapBakeType == LightmapBakeType.Mixed &&
  443. lightData.light.shadows != LightShadows.None &&
  444. m_MixedLightingSetup == MixedLightingSetup.None)
  445. {
  446. switch (lightBakingOutput.mixedLightingMode)
  447. {
  448. case MixedLightingMode.Subtractive:
  449. m_MixedLightingSetup = MixedLightingSetup.Subtractive;
  450. break;
  451. case MixedLightingMode.Shadowmask:
  452. m_MixedLightingSetup = MixedLightingSetup.ShadowMask;
  453. break;
  454. }
  455. }
  456. if (supportsLightLayers)
  457. {
  458. var additionalLightData = light.GetUniversalAdditionalLightData();
  459. lightLayerMask = RenderingLayerUtils.ToValidRenderingLayers(additionalLightData.renderingLayers);
  460. }
  461. }
  462. void SetupShaderLightConstants(UnsafeCommandBuffer cmd, ref CullingResults cullResults, UniversalLightData lightData)
  463. {
  464. m_MixedLightingSetup = MixedLightingSetup.None;
  465. // Main light has an optimized shader path for main light. This will benefit games that only care about a single light.
  466. // Universal pipeline also supports only a single shadow light, if available it will be the main light.
  467. SetupMainLightConstants(cmd, lightData);
  468. SetupAdditionalLightConstants(cmd, ref cullResults, lightData);
  469. }
  470. void SetupMainLightConstants(UnsafeCommandBuffer cmd, UniversalLightData lightData)
  471. {
  472. Vector4 lightPos, lightColor, lightAttenuation, lightSpotDir, lightOcclusionChannel;
  473. bool supportsLightLayers = lightData.supportsLightLayers;
  474. uint lightLayerMask;
  475. bool isSubtractive;
  476. InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, supportsLightLayers, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionChannel, out lightLayerMask, out isSubtractive);
  477. lightColor.w = isSubtractive ? 0f : 1f;
  478. cmd.SetGlobalVector(LightConstantBuffer._MainLightPosition, lightPos);
  479. cmd.SetGlobalVector(LightConstantBuffer._MainLightColor, lightColor);
  480. cmd.SetGlobalVector(LightConstantBuffer._MainLightOcclusionProbesChannel, lightOcclusionChannel);
  481. if (supportsLightLayers)
  482. cmd.SetGlobalInt(LightConstantBuffer._MainLightLayerMask, (int)lightLayerMask);
  483. }
  484. void SetupAdditionalLightConstants(UnsafeCommandBuffer cmd, ref CullingResults cullResults, UniversalLightData lightData)
  485. {
  486. bool supportsLightLayers = lightData.supportsLightLayers;
  487. var lights = lightData.visibleLights;
  488. int maxAdditionalLightsCount = UniversalRenderPipeline.maxVisibleAdditionalLights;
  489. int additionalLightsCount = SetupPerObjectLightIndices(cullResults, lightData);
  490. if (additionalLightsCount > 0)
  491. {
  492. if (m_UseStructuredBuffer)
  493. {
  494. NativeArray<ShaderInput.LightData> additionalLightsData = new NativeArray<ShaderInput.LightData>(additionalLightsCount, Allocator.Temp);
  495. for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
  496. {
  497. if (lightData.mainLightIndex != i)
  498. {
  499. ShaderInput.LightData data;
  500. InitializeLightConstants(lights, i, supportsLightLayers,
  501. out data.position, out data.color, out data.attenuation,
  502. out data.spotDirection, out data.occlusionProbeChannels,
  503. out data.layerMask, out _);
  504. additionalLightsData[lightIter] = data;
  505. lightIter++;
  506. }
  507. }
  508. var lightDataBuffer = ShaderData.instance.GetLightDataBuffer(additionalLightsCount);
  509. lightDataBuffer.SetData(additionalLightsData);
  510. int lightIndices = cullResults.lightAndReflectionProbeIndexCount;
  511. var lightIndicesBuffer = ShaderData.instance.GetLightIndicesBuffer(lightIndices);
  512. cmd.SetGlobalBuffer(m_AdditionalLightsBufferId, lightDataBuffer);
  513. cmd.SetGlobalBuffer(m_AdditionalLightsIndicesId, lightIndicesBuffer);
  514. additionalLightsData.Dispose();
  515. }
  516. else
  517. {
  518. for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
  519. {
  520. if (lightData.mainLightIndex != i)
  521. {
  522. InitializeLightConstants(
  523. lights,
  524. i,
  525. supportsLightLayers,
  526. out m_AdditionalLightPositions[lightIter],
  527. out m_AdditionalLightColors[lightIter],
  528. out m_AdditionalLightAttenuations[lightIter],
  529. out m_AdditionalLightSpotDirections[lightIter],
  530. out m_AdditionalLightOcclusionProbeChannels[lightIter],
  531. out uint lightLayerMask,
  532. out var isSubtractive);
  533. if (supportsLightLayers)
  534. m_AdditionalLightsLayerMasks[lightIter] = math.asfloat(lightLayerMask);
  535. m_AdditionalLightColors[lightIter].w = isSubtractive ? 1f : 0f;
  536. lightIter++;
  537. }
  538. }
  539. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsPosition, m_AdditionalLightPositions);
  540. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsColor, m_AdditionalLightColors);
  541. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsAttenuation, m_AdditionalLightAttenuations);
  542. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsSpotDir, m_AdditionalLightSpotDirections);
  543. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightOcclusionProbeChannel, m_AdditionalLightOcclusionProbeChannels);
  544. if (supportsLightLayers)
  545. cmd.SetGlobalFloatArray(LightConstantBuffer._AdditionalLightsLayerMasks, m_AdditionalLightsLayerMasks);
  546. }
  547. cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, new Vector4(lightData.maxPerObjectAdditionalLightsCount, 0.0f, 0.0f, 0.0f));
  548. }
  549. else
  550. {
  551. cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, Vector4.zero);
  552. }
  553. }
  554. int SetupPerObjectLightIndices(CullingResults cullResults, UniversalLightData lightData)
  555. {
  556. if (lightData.additionalLightsCount == 0 || m_UseForwardPlus)
  557. return lightData.additionalLightsCount;
  558. var perObjectLightIndexMap = cullResults.GetLightIndexMap(Allocator.Temp);
  559. int globalDirectionalLightsCount = 0;
  560. int additionalLightsCount = 0;
  561. // Disable all directional lights from the perobject light indices
  562. // Pipeline handles main light globally and there's no support for additional directional lights atm.
  563. int maxVisibleAdditionalLightsCount = UniversalRenderPipeline.maxVisibleAdditionalLights;
  564. int len = lightData.visibleLights.Length;
  565. for (int i = 0; i < len; ++i)
  566. {
  567. if (additionalLightsCount >= maxVisibleAdditionalLightsCount)
  568. break;
  569. if (i == lightData.mainLightIndex)
  570. {
  571. perObjectLightIndexMap[i] = -1;
  572. ++globalDirectionalLightsCount;
  573. }
  574. else
  575. {
  576. if (lightData.visibleLights[i].lightType == LightType.Directional ||
  577. lightData.visibleLights[i].lightType == LightType.Spot ||
  578. lightData.visibleLights[i].lightType == LightType.Point)
  579. {
  580. // Light type is supported
  581. perObjectLightIndexMap[i] -= globalDirectionalLightsCount;
  582. }
  583. else
  584. {
  585. // Light type is not supported. Skip the light.
  586. perObjectLightIndexMap[i] = -1;
  587. }
  588. ++additionalLightsCount;
  589. }
  590. }
  591. // Disable all remaining lights we cannot fit into the global light buffer.
  592. for (int i = globalDirectionalLightsCount + additionalLightsCount; i < perObjectLightIndexMap.Length; ++i)
  593. perObjectLightIndexMap[i] = -1;
  594. cullResults.SetLightIndexMap(perObjectLightIndexMap);
  595. if (m_UseStructuredBuffer && additionalLightsCount > 0)
  596. {
  597. int lightAndReflectionProbeIndices = cullResults.lightAndReflectionProbeIndexCount;
  598. Assertions.Assert.IsTrue(lightAndReflectionProbeIndices > 0, "Pipelines configures additional lights but per-object light and probe indices count is zero.");
  599. cullResults.FillLightAndReflectionProbeIndices(ShaderData.instance.GetLightIndicesBuffer(lightAndReflectionProbeIndices));
  600. }
  601. perObjectLightIndexMap.Dispose();
  602. return additionalLightsCount;
  603. }
  604. }
  605. }