ForwardLights.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. using Unity.Collections;
  2. using UnityEngine.PlayerLoop;
  3. using Unity.Jobs;
  4. using UnityEngine.Assertions;
  5. using Unity.Mathematics;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using UnityEditor;
  8. namespace UnityEngine.Rendering.Universal.Internal
  9. {
  10. /// <summary>
  11. /// Computes and submits lighting data to the GPU.
  12. /// </summary>
  13. public class ForwardLights
  14. {
  15. static class LightConstantBuffer
  16. {
  17. 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
  18. 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
  19. public static int _MainLightOcclusionProbesChannel; // Deferred?
  20. public static int _MainLightLayerMask;
  21. public static int _AdditionalLightsCount;
  22. public static int _AdditionalLightsPosition;
  23. public static int _AdditionalLightsColor;
  24. public static int _AdditionalLightsAttenuation;
  25. public static int _AdditionalLightsSpotDir;
  26. public static int _AdditionalLightOcclusionProbeChannel;
  27. public static int _AdditionalLightsLayerMasks;
  28. }
  29. int m_AdditionalLightsBufferId;
  30. int m_AdditionalLightsIndicesId;
  31. const string k_SetupLightConstants = "Setup Light Constants";
  32. private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(k_SetupLightConstants);
  33. MixedLightingSetup m_MixedLightingSetup;
  34. Vector4[] m_AdditionalLightPositions;
  35. Vector4[] m_AdditionalLightColors;
  36. Vector4[] m_AdditionalLightAttenuations;
  37. Vector4[] m_AdditionalLightSpotDirections;
  38. Vector4[] m_AdditionalLightOcclusionProbeChannels;
  39. float[] m_AdditionalLightsLayerMasks; // Unity has no support for binding uint arrays. We will use asuint() in the shader instead.
  40. bool m_UseStructuredBuffer;
  41. bool m_UseClusteredRendering;
  42. int m_DirectionalLightCount;
  43. int m_ActualTileWidth;
  44. int2 m_TileResolution;
  45. int m_RequestedTileWidth;
  46. float m_ZBinFactor;
  47. int m_ZBinOffset;
  48. JobHandle m_CullingHandle;
  49. NativeArray<ZBin> m_ZBins;
  50. NativeArray<uint> m_TileLightMasks;
  51. ComputeBuffer m_ZBinBuffer;
  52. ComputeBuffer m_TileBuffer;
  53. private LightCookieManager m_LightCookieManager;
  54. internal struct InitParams
  55. {
  56. public LightCookieManager lightCookieManager;
  57. public bool clusteredRendering;
  58. public int tileSize;
  59. static internal InitParams GetDefault()
  60. {
  61. InitParams p;
  62. {
  63. var settings = LightCookieManager.Settings.GetDefault();
  64. var asset = UniversalRenderPipeline.asset;
  65. if (asset)
  66. {
  67. settings.atlas.format = asset.additionalLightsCookieFormat;
  68. settings.atlas.resolution = asset.additionalLightsCookieResolution;
  69. }
  70. p.lightCookieManager = new LightCookieManager(ref settings);
  71. p.clusteredRendering = false;
  72. p.tileSize = 32;
  73. }
  74. return p;
  75. }
  76. }
  77. public ForwardLights() : this(InitParams.GetDefault()) { }
  78. internal ForwardLights(InitParams initParams)
  79. {
  80. if (initParams.clusteredRendering) Assert.IsTrue(math.ispow2(initParams.tileSize));
  81. m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
  82. m_UseClusteredRendering = initParams.clusteredRendering;
  83. LightConstantBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition");
  84. LightConstantBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor");
  85. LightConstantBuffer._MainLightOcclusionProbesChannel = Shader.PropertyToID("_MainLightOcclusionProbes");
  86. LightConstantBuffer._MainLightLayerMask = Shader.PropertyToID("_MainLightLayerMask");
  87. LightConstantBuffer._AdditionalLightsCount = Shader.PropertyToID("_AdditionalLightsCount");
  88. if (m_UseStructuredBuffer)
  89. {
  90. m_AdditionalLightsBufferId = Shader.PropertyToID("_AdditionalLightsBuffer");
  91. m_AdditionalLightsIndicesId = Shader.PropertyToID("_AdditionalLightsIndices");
  92. }
  93. else
  94. {
  95. LightConstantBuffer._AdditionalLightsPosition = Shader.PropertyToID("_AdditionalLightsPosition");
  96. LightConstantBuffer._AdditionalLightsColor = Shader.PropertyToID("_AdditionalLightsColor");
  97. LightConstantBuffer._AdditionalLightsAttenuation = Shader.PropertyToID("_AdditionalLightsAttenuation");
  98. LightConstantBuffer._AdditionalLightsSpotDir = Shader.PropertyToID("_AdditionalLightsSpotDir");
  99. LightConstantBuffer._AdditionalLightOcclusionProbeChannel = Shader.PropertyToID("_AdditionalLightsOcclusionProbes");
  100. LightConstantBuffer._AdditionalLightsLayerMasks = Shader.PropertyToID("_AdditionalLightsLayerMasks");
  101. int maxLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
  102. m_AdditionalLightPositions = new Vector4[maxLights];
  103. m_AdditionalLightColors = new Vector4[maxLights];
  104. m_AdditionalLightAttenuations = new Vector4[maxLights];
  105. m_AdditionalLightSpotDirections = new Vector4[maxLights];
  106. m_AdditionalLightOcclusionProbeChannels = new Vector4[maxLights];
  107. m_AdditionalLightsLayerMasks = new float[maxLights];
  108. }
  109. m_LightCookieManager = initParams.lightCookieManager;
  110. if (m_UseClusteredRendering)
  111. {
  112. m_ZBinBuffer = new ComputeBuffer(UniversalRenderPipeline.maxZBins / 4, UnsafeUtility.SizeOf<float4>(), ComputeBufferType.Constant, ComputeBufferMode.Dynamic);
  113. m_TileBuffer = new ComputeBuffer(UniversalRenderPipeline.maxTileVec4s, UnsafeUtility.SizeOf<float4>(), ComputeBufferType.Constant, ComputeBufferMode.Dynamic);
  114. m_RequestedTileWidth = initParams.tileSize;
  115. }
  116. }
  117. internal void ProcessLights(ref RenderingData renderingData)
  118. {
  119. if (m_UseClusteredRendering)
  120. {
  121. var camera = renderingData.cameraData.camera;
  122. var screenResolution = math.int2(renderingData.cameraData.pixelWidth, renderingData.cameraData.pixelHeight);
  123. var lightCount = renderingData.lightData.visibleLights.Length;
  124. var lightOffset = 0;
  125. while (lightOffset < lightCount && renderingData.lightData.visibleLights[lightOffset].lightType == LightType.Directional)
  126. {
  127. lightOffset++;
  128. }
  129. if (lightOffset == lightCount) lightOffset = 0;
  130. lightCount -= lightOffset;
  131. m_DirectionalLightCount = lightOffset;
  132. if (renderingData.lightData.mainLightIndex != -1) m_DirectionalLightCount -= 1;
  133. var visibleLights = renderingData.lightData.visibleLights.GetSubArray(lightOffset, lightCount);
  134. var lightsPerTile = UniversalRenderPipeline.lightsPerTile;
  135. var wordsPerTile = lightsPerTile / 32;
  136. m_ActualTileWidth = m_RequestedTileWidth >> 1;
  137. do
  138. {
  139. m_ActualTileWidth = m_ActualTileWidth << 1;
  140. m_TileResolution = (screenResolution + m_ActualTileWidth - 1) / m_ActualTileWidth;
  141. }
  142. while ((m_TileResolution.x * m_TileResolution.y * wordsPerTile) > (UniversalRenderPipeline.maxTileVec4s * 4));
  143. var fovHalfHeight = math.tan(math.radians(camera.fieldOfView * 0.5f));
  144. // TODO: Make this work with VR
  145. var fovHalfWidth = fovHalfHeight * (float)screenResolution.x / (float)screenResolution.y;
  146. var maxZFactor = (float)UniversalRenderPipeline.maxZBins / (math.sqrt(camera.farClipPlane) - math.sqrt(camera.nearClipPlane));
  147. m_ZBinFactor = maxZFactor;
  148. m_ZBinOffset = (int)(math.sqrt(camera.nearClipPlane) * m_ZBinFactor);
  149. var binCount = (int)(math.sqrt(camera.farClipPlane) * m_ZBinFactor) - m_ZBinOffset;
  150. // Must be a multiple of 4 to be able to alias to vec4
  151. binCount = ((binCount + 3) / 4) * 4;
  152. binCount = math.min(UniversalRenderPipeline.maxZBins, binCount);
  153. m_ZBins = new NativeArray<ZBin>(binCount, Allocator.TempJob);
  154. Assert.AreEqual(UnsafeUtility.SizeOf<uint>(), UnsafeUtility.SizeOf<ZBin>());
  155. using var minMaxZs = new NativeArray<LightMinMaxZ>(lightCount, Allocator.TempJob);
  156. // We allocate double array length because the sorting algorithm needs swap space to work in.
  157. using var meanZs = new NativeArray<float>(lightCount * 2, Allocator.TempJob);
  158. Matrix4x4 worldToViewMatrix = renderingData.cameraData.GetViewMatrix();
  159. var minMaxZJob = new MinMaxZJob
  160. {
  161. worldToViewMatrix = worldToViewMatrix,
  162. lights = visibleLights,
  163. minMaxZs = minMaxZs,
  164. meanZs = meanZs
  165. };
  166. // Innerloop batch count of 32 is not special, just a handwavy amount to not have too much scheduling overhead nor too little parallelism.
  167. var minMaxZHandle = minMaxZJob.ScheduleParallel(lightCount, 32, new JobHandle());
  168. // We allocate double array length because the sorting algorithm needs swap space to work in.
  169. using var indices = new NativeArray<int>(lightCount * 2, Allocator.TempJob);
  170. var radixSortJob = new RadixSortJob
  171. {
  172. // Floats can be sorted bitwise with no special handling if positive floats only
  173. keys = meanZs.Reinterpret<uint>(),
  174. indices = indices
  175. };
  176. var zSortHandle = radixSortJob.Schedule(minMaxZHandle);
  177. var reorderedLights = new NativeArray<VisibleLight>(lightCount, Allocator.TempJob);
  178. var reorderedMinMaxZs = new NativeArray<LightMinMaxZ>(lightCount, Allocator.TempJob);
  179. var reorderLightsJob = new ReorderJob<VisibleLight> { indices = indices, input = visibleLights, output = reorderedLights };
  180. var reorderLightsHandle = reorderLightsJob.ScheduleParallel(lightCount, 32, zSortHandle);
  181. var reorderMinMaxZsJob = new ReorderJob<LightMinMaxZ> { indices = indices, input = minMaxZs, output = reorderedMinMaxZs };
  182. var reorderMinMaxZsHandle = reorderMinMaxZsJob.ScheduleParallel(lightCount, 32, zSortHandle);
  183. var reorderHandle = JobHandle.CombineDependencies(
  184. reorderLightsHandle,
  185. reorderMinMaxZsHandle
  186. );
  187. JobHandle.ScheduleBatchedJobs();
  188. LightExtractionJob lightExtractionJob;
  189. lightExtractionJob.lights = reorderedLights;
  190. var lightTypes = lightExtractionJob.lightTypes = new NativeArray<LightType>(lightCount, Allocator.TempJob);
  191. var radiuses = lightExtractionJob.radiuses = new NativeArray<float>(lightCount, Allocator.TempJob);
  192. var directions = lightExtractionJob.directions = new NativeArray<float3>(lightCount, Allocator.TempJob);
  193. var positions = lightExtractionJob.positions = new NativeArray<float3>(lightCount, Allocator.TempJob);
  194. var coneRadiuses = lightExtractionJob.coneRadiuses = new NativeArray<float>(lightCount, Allocator.TempJob);
  195. var lightExtractionHandle = lightExtractionJob.ScheduleParallel(lightCount, 32, reorderHandle);
  196. var zBinningJob = new ZBinningJob
  197. {
  198. bins = m_ZBins,
  199. minMaxZs = reorderedMinMaxZs,
  200. binOffset = m_ZBinOffset,
  201. zFactor = m_ZBinFactor
  202. };
  203. var zBinningHandle = zBinningJob.ScheduleParallel((binCount + ZBinningJob.batchCount - 1) / ZBinningJob.batchCount, 1, reorderHandle);
  204. reorderedMinMaxZs.Dispose(zBinningHandle);
  205. // Must be a multiple of 4 to be able to alias to vec4
  206. var lightMasksLength = (((wordsPerTile) * m_TileResolution + 3) / 4) * 4;
  207. var horizontalLightMasks = new NativeArray<uint>(lightMasksLength.y, Allocator.TempJob);
  208. var verticalLightMasks = new NativeArray<uint>(lightMasksLength.x, Allocator.TempJob);
  209. // Vertical slices along the x-axis
  210. var verticalJob = new SliceCullingJob
  211. {
  212. scale = (float)m_ActualTileWidth / (float)screenResolution.x,
  213. viewOrigin = camera.transform.position,
  214. viewForward = camera.transform.forward,
  215. viewRight = camera.transform.right * fovHalfWidth,
  216. viewUp = camera.transform.up * fovHalfHeight,
  217. lightTypes = lightTypes,
  218. radiuses = radiuses,
  219. directions = directions,
  220. positions = positions,
  221. coneRadiuses = coneRadiuses,
  222. lightsPerTile = lightsPerTile,
  223. sliceLightMasks = verticalLightMasks
  224. };
  225. var verticalHandle = verticalJob.ScheduleParallel(m_TileResolution.x, 1, lightExtractionHandle);
  226. // Horizontal slices along the y-axis
  227. var horizontalJob = verticalJob;
  228. horizontalJob.scale = (float)m_ActualTileWidth / (float)screenResolution.y;
  229. horizontalJob.viewRight = camera.transform.up * fovHalfHeight;
  230. horizontalJob.viewUp = -camera.transform.right * fovHalfWidth;
  231. horizontalJob.sliceLightMasks = horizontalLightMasks;
  232. var horizontalHandle = horizontalJob.ScheduleParallel(m_TileResolution.y, 1, lightExtractionHandle);
  233. var slicesHandle = JobHandle.CombineDependencies(horizontalHandle, verticalHandle);
  234. m_TileLightMasks = new NativeArray<uint>(((m_TileResolution.x * m_TileResolution.y * (wordsPerTile) + 3) / 4) * 4, Allocator.TempJob);
  235. var sliceCombineJob = new SliceCombineJob
  236. {
  237. tileResolution = m_TileResolution,
  238. wordsPerTile = wordsPerTile,
  239. sliceLightMasksH = horizontalLightMasks,
  240. sliceLightMasksV = verticalLightMasks,
  241. lightMasks = m_TileLightMasks
  242. };
  243. var sliceCombineHandle = sliceCombineJob.ScheduleParallel(m_TileResolution.y, 1, slicesHandle);
  244. m_CullingHandle = JobHandle.CombineDependencies(sliceCombineHandle, zBinningHandle);
  245. reorderHandle.Complete();
  246. NativeArray<VisibleLight>.Copy(reorderedLights, 0, renderingData.lightData.visibleLights, lightOffset, lightCount);
  247. var tempBias = new NativeArray<Vector4>(lightCount, Allocator.Temp);
  248. var tempResolution = new NativeArray<int>(lightCount, Allocator.Temp);
  249. var tempIndices = new NativeArray<int>(lightCount, Allocator.Temp);
  250. for (var i = 0; i < lightCount; i++)
  251. {
  252. tempBias[indices[i]] = renderingData.shadowData.bias[lightOffset + i];
  253. tempResolution[indices[i]] = renderingData.shadowData.resolution[lightOffset + i];
  254. tempIndices[indices[i]] = lightOffset + i;
  255. }
  256. for (var i = 0; i < lightCount; i++)
  257. {
  258. renderingData.shadowData.bias[i + lightOffset] = tempBias[i];
  259. renderingData.shadowData.resolution[i + lightOffset] = tempResolution[i];
  260. renderingData.lightData.originalIndices[i + lightOffset] = tempIndices[i];
  261. }
  262. tempBias.Dispose();
  263. tempResolution.Dispose();
  264. tempIndices.Dispose();
  265. lightTypes.Dispose(m_CullingHandle);
  266. radiuses.Dispose(m_CullingHandle);
  267. directions.Dispose(m_CullingHandle);
  268. positions.Dispose(m_CullingHandle);
  269. coneRadiuses.Dispose(m_CullingHandle);
  270. reorderedLights.Dispose(m_CullingHandle);
  271. horizontalLightMasks.Dispose(m_CullingHandle);
  272. verticalLightMasks.Dispose(m_CullingHandle);
  273. JobHandle.ScheduleBatchedJobs();
  274. }
  275. }
  276. public void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
  277. {
  278. int additionalLightsCount = renderingData.lightData.additionalLightsCount;
  279. bool additionalLightsPerVertex = renderingData.lightData.shadeAdditionalLightsPerVertex;
  280. CommandBuffer cmd = CommandBufferPool.Get();
  281. using (new ProfilingScope(null, m_ProfilingSampler))
  282. {
  283. var useClusteredRendering = m_UseClusteredRendering;
  284. if (useClusteredRendering)
  285. {
  286. m_CullingHandle.Complete();
  287. m_ZBinBuffer.SetData(m_ZBins.Reinterpret<float4>(UnsafeUtility.SizeOf<ZBin>()), 0, 0, m_ZBins.Length / 4);
  288. m_TileBuffer.SetData(m_TileLightMasks.Reinterpret<float4>(UnsafeUtility.SizeOf<uint>()), 0, 0, m_TileLightMasks.Length / 4);
  289. cmd.SetGlobalInteger("_AdditionalLightsDirectionalCount", m_DirectionalLightCount);
  290. cmd.SetGlobalInteger("_AdditionalLightsZBinOffset", m_ZBinOffset);
  291. cmd.SetGlobalFloat("_AdditionalLightsZBinScale", m_ZBinFactor);
  292. cmd.SetGlobalVector("_AdditionalLightsTileScale", renderingData.cameraData.pixelRect.size / (float)m_ActualTileWidth);
  293. cmd.SetGlobalInteger("_AdditionalLightsTileCountX", m_TileResolution.x);
  294. cmd.SetGlobalConstantBuffer(m_ZBinBuffer, "AdditionalLightsZBins", 0, m_ZBins.Length * 4);
  295. cmd.SetGlobalConstantBuffer(m_TileBuffer, "AdditionalLightsTiles", 0, m_TileLightMasks.Length * 4);
  296. m_ZBins.Dispose();
  297. m_TileLightMasks.Dispose();
  298. }
  299. SetupShaderLightConstants(cmd, ref renderingData);
  300. bool lightCountCheck = (renderingData.cameraData.renderer.stripAdditionalLightOffVariants && renderingData.lightData.supportsAdditionalLights) || additionalLightsCount > 0;
  301. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsVertex,
  302. lightCountCheck && additionalLightsPerVertex && !useClusteredRendering);
  303. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsPixel,
  304. lightCountCheck && !additionalLightsPerVertex && !useClusteredRendering);
  305. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ClusteredRendering,
  306. useClusteredRendering);
  307. bool isShadowMask = renderingData.lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.ShadowMask;
  308. bool isShadowMaskAlways = isShadowMask && QualitySettings.shadowmaskMode == ShadowmaskMode.Shadowmask;
  309. bool isSubtractive = renderingData.lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.Subtractive;
  310. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.LightmapShadowMixing, isSubtractive || isShadowMaskAlways);
  311. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ShadowsShadowMask, isShadowMask);
  312. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MixedLightingSubtractive, isSubtractive); // Backward compatibility
  313. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ReflectionProbeBlending, renderingData.lightData.reflectionProbeBlending);
  314. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ReflectionProbeBoxProjection, renderingData.lightData.reflectionProbeBoxProjection);
  315. bool lightLayers = renderingData.lightData.supportsLightLayers;
  316. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.LightLayers, lightLayers);
  317. m_LightCookieManager.Setup(context, cmd, ref renderingData.lightData);
  318. }
  319. context.ExecuteCommandBuffer(cmd);
  320. CommandBufferPool.Release(cmd);
  321. }
  322. internal void Cleanup()
  323. {
  324. if (m_UseClusteredRendering)
  325. {
  326. m_ZBinBuffer.Dispose();
  327. m_TileBuffer.Dispose();
  328. }
  329. }
  330. void InitializeLightConstants(NativeArray<VisibleLight> lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightAttenuation, out Vector4 lightSpotDir, out Vector4 lightOcclusionProbeChannel, out uint lightLayerMask)
  331. {
  332. UniversalRenderPipeline.InitializeLightConstants_Common(lights, lightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionProbeChannel);
  333. lightLayerMask = 0;
  334. // When no lights are visible, main light will be set to -1.
  335. // In this case we initialize it to default values and return
  336. if (lightIndex < 0)
  337. return;
  338. VisibleLight lightData = lights[lightIndex];
  339. Light light = lightData.light;
  340. if (light == null)
  341. return;
  342. if (light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed &&
  343. lightData.light.shadows != LightShadows.None &&
  344. m_MixedLightingSetup == MixedLightingSetup.None)
  345. {
  346. switch (light.bakingOutput.mixedLightingMode)
  347. {
  348. case MixedLightingMode.Subtractive:
  349. m_MixedLightingSetup = MixedLightingSetup.Subtractive;
  350. break;
  351. case MixedLightingMode.Shadowmask:
  352. m_MixedLightingSetup = MixedLightingSetup.ShadowMask;
  353. break;
  354. }
  355. }
  356. var additionalLightData = light.GetUniversalAdditionalLightData();
  357. lightLayerMask = (uint)additionalLightData.lightLayerMask;
  358. }
  359. void SetupShaderLightConstants(CommandBuffer cmd, ref RenderingData renderingData)
  360. {
  361. m_MixedLightingSetup = MixedLightingSetup.None;
  362. // Main light has an optimized shader path for main light. This will benefit games that only care about a single light.
  363. // Universal pipeline also supports only a single shadow light, if available it will be the main light.
  364. SetupMainLightConstants(cmd, ref renderingData.lightData);
  365. SetupAdditionalLightConstants(cmd, ref renderingData);
  366. }
  367. void SetupMainLightConstants(CommandBuffer cmd, ref LightData lightData)
  368. {
  369. Vector4 lightPos, lightColor, lightAttenuation, lightSpotDir, lightOcclusionChannel;
  370. uint lightLayerMask;
  371. InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionChannel, out lightLayerMask);
  372. cmd.SetGlobalVector(LightConstantBuffer._MainLightPosition, lightPos);
  373. cmd.SetGlobalVector(LightConstantBuffer._MainLightColor, lightColor);
  374. cmd.SetGlobalVector(LightConstantBuffer._MainLightOcclusionProbesChannel, lightOcclusionChannel);
  375. cmd.SetGlobalInt(LightConstantBuffer._MainLightLayerMask, (int)lightLayerMask);
  376. }
  377. void SetupAdditionalLightConstants(CommandBuffer cmd, ref RenderingData renderingData)
  378. {
  379. ref LightData lightData = ref renderingData.lightData;
  380. var cullResults = renderingData.cullResults;
  381. var lights = lightData.visibleLights;
  382. int maxAdditionalLightsCount = UniversalRenderPipeline.maxVisibleAdditionalLights;
  383. int additionalLightsCount = SetupPerObjectLightIndices(cullResults, ref lightData);
  384. if (additionalLightsCount > 0)
  385. {
  386. if (m_UseStructuredBuffer)
  387. {
  388. NativeArray<ShaderInput.LightData> additionalLightsData = new NativeArray<ShaderInput.LightData>(additionalLightsCount, Allocator.Temp);
  389. for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
  390. {
  391. VisibleLight light = lights[i];
  392. if (lightData.mainLightIndex != i)
  393. {
  394. ShaderInput.LightData data;
  395. InitializeLightConstants(lights, i,
  396. out data.position, out data.color, out data.attenuation,
  397. out data.spotDirection, out data.occlusionProbeChannels,
  398. out data.layerMask);
  399. additionalLightsData[lightIter] = data;
  400. lightIter++;
  401. }
  402. }
  403. var lightDataBuffer = ShaderData.instance.GetLightDataBuffer(additionalLightsCount);
  404. lightDataBuffer.SetData(additionalLightsData);
  405. int lightIndices = cullResults.lightAndReflectionProbeIndexCount;
  406. var lightIndicesBuffer = ShaderData.instance.GetLightIndicesBuffer(lightIndices);
  407. cmd.SetGlobalBuffer(m_AdditionalLightsBufferId, lightDataBuffer);
  408. cmd.SetGlobalBuffer(m_AdditionalLightsIndicesId, lightIndicesBuffer);
  409. additionalLightsData.Dispose();
  410. }
  411. else
  412. {
  413. for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
  414. {
  415. VisibleLight light = lights[i];
  416. if (lightData.mainLightIndex != i)
  417. {
  418. uint lightLayerMask;
  419. InitializeLightConstants(lights, i, out m_AdditionalLightPositions[lightIter],
  420. out m_AdditionalLightColors[lightIter],
  421. out m_AdditionalLightAttenuations[lightIter],
  422. out m_AdditionalLightSpotDirections[lightIter],
  423. out m_AdditionalLightOcclusionProbeChannels[lightIter],
  424. out lightLayerMask);
  425. m_AdditionalLightsLayerMasks[lightIter] = Unity.Mathematics.math.asfloat(lightLayerMask);
  426. lightIter++;
  427. }
  428. }
  429. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsPosition, m_AdditionalLightPositions);
  430. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsColor, m_AdditionalLightColors);
  431. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsAttenuation, m_AdditionalLightAttenuations);
  432. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsSpotDir, m_AdditionalLightSpotDirections);
  433. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightOcclusionProbeChannel, m_AdditionalLightOcclusionProbeChannels);
  434. cmd.SetGlobalFloatArray(LightConstantBuffer._AdditionalLightsLayerMasks, m_AdditionalLightsLayerMasks);
  435. }
  436. cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, new Vector4(lightData.maxPerObjectAdditionalLightsCount,
  437. 0.0f, 0.0f, 0.0f));
  438. }
  439. else
  440. {
  441. cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, Vector4.zero);
  442. }
  443. }
  444. int SetupPerObjectLightIndices(CullingResults cullResults, ref LightData lightData)
  445. {
  446. if (lightData.additionalLightsCount == 0)
  447. return lightData.additionalLightsCount;
  448. var visibleLights = lightData.visibleLights;
  449. var perObjectLightIndexMap = cullResults.GetLightIndexMap(Allocator.Temp);
  450. int globalDirectionalLightsCount = 0;
  451. int additionalLightsCount = 0;
  452. // Disable all directional lights from the perobject light indices
  453. // Pipeline handles main light globally and there's no support for additional directional lights atm.
  454. for (int i = 0; i < visibleLights.Length; ++i)
  455. {
  456. if (additionalLightsCount >= UniversalRenderPipeline.maxVisibleAdditionalLights)
  457. break;
  458. VisibleLight light = visibleLights[i];
  459. if (i == lightData.mainLightIndex)
  460. {
  461. perObjectLightIndexMap[i] = -1;
  462. ++globalDirectionalLightsCount;
  463. }
  464. else
  465. {
  466. perObjectLightIndexMap[i] -= globalDirectionalLightsCount;
  467. ++additionalLightsCount;
  468. }
  469. }
  470. // Disable all remaining lights we cannot fit into the global light buffer.
  471. for (int i = globalDirectionalLightsCount + additionalLightsCount; i < perObjectLightIndexMap.Length; ++i)
  472. perObjectLightIndexMap[i] = -1;
  473. cullResults.SetLightIndexMap(perObjectLightIndexMap);
  474. if (m_UseStructuredBuffer && additionalLightsCount > 0)
  475. {
  476. int lightAndReflectionProbeIndices = cullResults.lightAndReflectionProbeIndexCount;
  477. Assertions.Assert.IsTrue(lightAndReflectionProbeIndices > 0, "Pipelines configures additional lights but per-object light and probe indices count is zero.");
  478. cullResults.FillLightAndReflectionProbeIndices(ShaderData.instance.GetLightIndicesBuffer(lightAndReflectionProbeIndices));
  479. }
  480. perObjectLightIndexMap.Dispose();
  481. return additionalLightsCount;
  482. }
  483. }
  484. }