ForwardLights.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using UnityEngine.Experimental.GlobalIllumination;
  2. using Unity.Collections;
  3. namespace UnityEngine.Rendering.Universal.Internal
  4. {
  5. /// <summary>
  6. /// Computes and submits lighting data to the GPU.
  7. /// </summary>
  8. public class ForwardLights
  9. {
  10. static class LightConstantBuffer
  11. {
  12. 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
  13. 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
  14. public static int _MainLightOcclusionProbesChannel; // Deferred?
  15. public static int _AdditionalLightsCount;
  16. public static int _AdditionalLightsPosition;
  17. public static int _AdditionalLightsColor;
  18. public static int _AdditionalLightsAttenuation;
  19. public static int _AdditionalLightsSpotDir;
  20. public static int _AdditionalLightOcclusionProbeChannel;
  21. }
  22. int m_AdditionalLightsBufferId;
  23. int m_AdditionalLightsIndicesId;
  24. const string k_SetupLightConstants = "Setup Light Constants";
  25. private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(k_SetupLightConstants);
  26. MixedLightingSetup m_MixedLightingSetup;
  27. Vector4[] m_AdditionalLightPositions;
  28. Vector4[] m_AdditionalLightColors;
  29. Vector4[] m_AdditionalLightAttenuations;
  30. Vector4[] m_AdditionalLightSpotDirections;
  31. Vector4[] m_AdditionalLightOcclusionProbeChannels;
  32. bool m_UseStructuredBuffer;
  33. public ForwardLights()
  34. {
  35. m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
  36. LightConstantBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition");
  37. LightConstantBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor");
  38. LightConstantBuffer._MainLightOcclusionProbesChannel = Shader.PropertyToID("_MainLightOcclusionProbes");
  39. LightConstantBuffer._AdditionalLightsCount = Shader.PropertyToID("_AdditionalLightsCount");
  40. if (m_UseStructuredBuffer)
  41. {
  42. m_AdditionalLightsBufferId = Shader.PropertyToID("_AdditionalLightsBuffer");
  43. m_AdditionalLightsIndicesId = Shader.PropertyToID("_AdditionalLightsIndices");
  44. }
  45. else
  46. {
  47. LightConstantBuffer._AdditionalLightsPosition = Shader.PropertyToID("_AdditionalLightsPosition");
  48. LightConstantBuffer._AdditionalLightsColor = Shader.PropertyToID("_AdditionalLightsColor");
  49. LightConstantBuffer._AdditionalLightsAttenuation = Shader.PropertyToID("_AdditionalLightsAttenuation");
  50. LightConstantBuffer._AdditionalLightsSpotDir = Shader.PropertyToID("_AdditionalLightsSpotDir");
  51. LightConstantBuffer._AdditionalLightOcclusionProbeChannel = Shader.PropertyToID("_AdditionalLightsOcclusionProbes");
  52. int maxLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
  53. m_AdditionalLightPositions = new Vector4[maxLights];
  54. m_AdditionalLightColors = new Vector4[maxLights];
  55. m_AdditionalLightAttenuations = new Vector4[maxLights];
  56. m_AdditionalLightSpotDirections = new Vector4[maxLights];
  57. m_AdditionalLightOcclusionProbeChannels = new Vector4[maxLights];
  58. }
  59. }
  60. public void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
  61. {
  62. int additionalLightsCount = renderingData.lightData.additionalLightsCount;
  63. bool additionalLightsPerVertex = renderingData.lightData.shadeAdditionalLightsPerVertex;
  64. CommandBuffer cmd = CommandBufferPool.Get();
  65. using (new ProfilingScope(cmd, m_ProfilingSampler))
  66. {
  67. SetupShaderLightConstants(cmd, ref renderingData);
  68. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsVertex,
  69. additionalLightsCount > 0 && additionalLightsPerVertex);
  70. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsPixel,
  71. additionalLightsCount > 0 && !additionalLightsPerVertex);
  72. bool isShadowMask = renderingData.lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.ShadowMask;
  73. bool isShadowMaskAlways = isShadowMask && QualitySettings.shadowmaskMode == ShadowmaskMode.Shadowmask;
  74. bool isSubtractive = renderingData.lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.Subtractive;
  75. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.LightmapShadowMixing, isSubtractive || isShadowMaskAlways);
  76. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ShadowsShadowMask, isShadowMask);
  77. CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MixedLightingSubtractive, isSubtractive); // Backward compatibility
  78. }
  79. context.ExecuteCommandBuffer(cmd);
  80. CommandBufferPool.Release(cmd);
  81. }
  82. void InitializeLightConstants(NativeArray<VisibleLight> lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightAttenuation, out Vector4 lightSpotDir, out Vector4 lightOcclusionProbeChannel)
  83. {
  84. UniversalRenderPipeline.InitializeLightConstants_Common(lights, lightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionProbeChannel);
  85. // When no lights are visible, main light will be set to -1.
  86. // In this case we initialize it to default values and return
  87. if (lightIndex < 0)
  88. return;
  89. VisibleLight lightData = lights[lightIndex];
  90. Light light = lightData.light;
  91. if (light == null)
  92. return;
  93. if (light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed &&
  94. lightData.light.shadows != LightShadows.None &&
  95. m_MixedLightingSetup == MixedLightingSetup.None)
  96. {
  97. switch (light.bakingOutput.mixedLightingMode)
  98. {
  99. case MixedLightingMode.Subtractive:
  100. m_MixedLightingSetup = MixedLightingSetup.Subtractive;
  101. break;
  102. case MixedLightingMode.Shadowmask:
  103. m_MixedLightingSetup = MixedLightingSetup.ShadowMask;
  104. break;
  105. }
  106. }
  107. }
  108. void SetupShaderLightConstants(CommandBuffer cmd, ref RenderingData renderingData)
  109. {
  110. m_MixedLightingSetup = MixedLightingSetup.None;
  111. // Main light has an optimized shader path for main light. This will benefit games that only care about a single light.
  112. // Universal pipeline also supports only a single shadow light, if available it will be the main light.
  113. SetupMainLightConstants(cmd, ref renderingData.lightData);
  114. SetupAdditionalLightConstants(cmd, ref renderingData);
  115. }
  116. void SetupMainLightConstants(CommandBuffer cmd, ref LightData lightData)
  117. {
  118. Vector4 lightPos, lightColor, lightAttenuation, lightSpotDir, lightOcclusionChannel;
  119. InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionChannel);
  120. cmd.SetGlobalVector(LightConstantBuffer._MainLightPosition, lightPos);
  121. cmd.SetGlobalVector(LightConstantBuffer._MainLightColor, lightColor);
  122. cmd.SetGlobalVector(LightConstantBuffer._MainLightOcclusionProbesChannel, lightOcclusionChannel);
  123. }
  124. void SetupAdditionalLightConstants(CommandBuffer cmd, ref RenderingData renderingData)
  125. {
  126. ref LightData lightData = ref renderingData.lightData;
  127. var cullResults = renderingData.cullResults;
  128. var lights = lightData.visibleLights;
  129. int maxAdditionalLightsCount = UniversalRenderPipeline.maxVisibleAdditionalLights;
  130. int additionalLightsCount = SetupPerObjectLightIndices(cullResults, ref lightData);
  131. if (additionalLightsCount > 0)
  132. {
  133. if (m_UseStructuredBuffer)
  134. {
  135. NativeArray<ShaderInput.LightData> additionalLightsData = new NativeArray<ShaderInput.LightData>(additionalLightsCount, Allocator.Temp);
  136. for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
  137. {
  138. VisibleLight light = lights[i];
  139. if (lightData.mainLightIndex != i)
  140. {
  141. ShaderInput.LightData data;
  142. InitializeLightConstants(lights, i,
  143. out data.position, out data.color, out data.attenuation,
  144. out data.spotDirection, out data.occlusionProbeChannels);
  145. additionalLightsData[lightIter] = data;
  146. lightIter++;
  147. }
  148. }
  149. var lightDataBuffer = ShaderData.instance.GetLightDataBuffer(additionalLightsCount);
  150. lightDataBuffer.SetData(additionalLightsData);
  151. int lightIndices = cullResults.lightAndReflectionProbeIndexCount;
  152. var lightIndicesBuffer = ShaderData.instance.GetLightIndicesBuffer(lightIndices);
  153. cmd.SetGlobalBuffer(m_AdditionalLightsBufferId, lightDataBuffer);
  154. cmd.SetGlobalBuffer(m_AdditionalLightsIndicesId, lightIndicesBuffer);
  155. additionalLightsData.Dispose();
  156. }
  157. else
  158. {
  159. for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
  160. {
  161. VisibleLight light = lights[i];
  162. if (lightData.mainLightIndex != i)
  163. {
  164. InitializeLightConstants(lights, i, out m_AdditionalLightPositions[lightIter],
  165. out m_AdditionalLightColors[lightIter],
  166. out m_AdditionalLightAttenuations[lightIter],
  167. out m_AdditionalLightSpotDirections[lightIter],
  168. out m_AdditionalLightOcclusionProbeChannels[lightIter]);
  169. lightIter++;
  170. }
  171. }
  172. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsPosition, m_AdditionalLightPositions);
  173. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsColor, m_AdditionalLightColors);
  174. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsAttenuation, m_AdditionalLightAttenuations);
  175. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsSpotDir, m_AdditionalLightSpotDirections);
  176. cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightOcclusionProbeChannel, m_AdditionalLightOcclusionProbeChannels);
  177. }
  178. cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, new Vector4(lightData.maxPerObjectAdditionalLightsCount,
  179. 0.0f, 0.0f, 0.0f));
  180. }
  181. else
  182. {
  183. cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, Vector4.zero);
  184. }
  185. }
  186. int SetupPerObjectLightIndices(CullingResults cullResults, ref LightData lightData)
  187. {
  188. if (lightData.additionalLightsCount == 0)
  189. return lightData.additionalLightsCount;
  190. var visibleLights = lightData.visibleLights;
  191. var perObjectLightIndexMap = cullResults.GetLightIndexMap(Allocator.Temp);
  192. int globalDirectionalLightsCount = 0;
  193. int additionalLightsCount = 0;
  194. // Disable all directional lights from the perobject light indices
  195. // Pipeline handles main light globally and there's no support for additional directional lights atm.
  196. for (int i = 0; i < visibleLights.Length; ++i)
  197. {
  198. if (additionalLightsCount >= UniversalRenderPipeline.maxVisibleAdditionalLights)
  199. break;
  200. VisibleLight light = visibleLights[i];
  201. if (i == lightData.mainLightIndex)
  202. {
  203. perObjectLightIndexMap[i] = -1;
  204. ++globalDirectionalLightsCount;
  205. }
  206. else
  207. {
  208. perObjectLightIndexMap[i] -= globalDirectionalLightsCount;
  209. ++additionalLightsCount;
  210. }
  211. }
  212. // Disable all remaining lights we cannot fit into the global light buffer.
  213. for (int i = globalDirectionalLightsCount + additionalLightsCount; i < perObjectLightIndexMap.Length; ++i)
  214. perObjectLightIndexMap[i] = -1;
  215. cullResults.SetLightIndexMap(perObjectLightIndexMap);
  216. if (m_UseStructuredBuffer && additionalLightsCount > 0)
  217. {
  218. int lightAndReflectionProbeIndices = cullResults.lightAndReflectionProbeIndexCount;
  219. Assertions.Assert.IsTrue(lightAndReflectionProbeIndices > 0, "Pipelines configures additional lights but per-object light and probe indices count is zero.");
  220. cullResults.FillLightAndReflectionProbeIndices(ShaderData.instance.GetLightIndicesBuffer(lightAndReflectionProbeIndices));
  221. }
  222. perObjectLightIndexMap.Dispose();
  223. return additionalLightsCount;
  224. }
  225. }
  226. }