using Unity.Collections;
using UnityEngine.PlayerLoop;
using Unity.Jobs;
using UnityEngine.Assertions;
using Unity.Mathematics;
using Unity.Collections.LowLevel.Unsafe;
using UnityEditor;
namespace UnityEngine.Rendering.Universal.Internal
{
///
/// Computes and submits lighting data to the GPU.
///
public class ForwardLights
{
static class LightConstantBuffer
{
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
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
public static int _MainLightOcclusionProbesChannel; // Deferred?
public static int _MainLightLayerMask;
public static int _AdditionalLightsCount;
public static int _AdditionalLightsPosition;
public static int _AdditionalLightsColor;
public static int _AdditionalLightsAttenuation;
public static int _AdditionalLightsSpotDir;
public static int _AdditionalLightOcclusionProbeChannel;
public static int _AdditionalLightsLayerMasks;
}
int m_AdditionalLightsBufferId;
int m_AdditionalLightsIndicesId;
const string k_SetupLightConstants = "Setup Light Constants";
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(k_SetupLightConstants);
MixedLightingSetup m_MixedLightingSetup;
Vector4[] m_AdditionalLightPositions;
Vector4[] m_AdditionalLightColors;
Vector4[] m_AdditionalLightAttenuations;
Vector4[] m_AdditionalLightSpotDirections;
Vector4[] m_AdditionalLightOcclusionProbeChannels;
float[] m_AdditionalLightsLayerMasks; // Unity has no support for binding uint arrays. We will use asuint() in the shader instead.
bool m_UseStructuredBuffer;
bool m_UseClusteredRendering;
int m_DirectionalLightCount;
int m_ActualTileWidth;
int2 m_TileResolution;
int m_RequestedTileWidth;
float m_ZBinFactor;
int m_ZBinOffset;
JobHandle m_CullingHandle;
NativeArray m_ZBins;
NativeArray m_TileLightMasks;
ComputeBuffer m_ZBinBuffer;
ComputeBuffer m_TileBuffer;
private LightCookieManager m_LightCookieManager;
internal struct InitParams
{
public LightCookieManager lightCookieManager;
public bool clusteredRendering;
public int tileSize;
static internal InitParams GetDefault()
{
InitParams p;
{
var settings = LightCookieManager.Settings.GetDefault();
var asset = UniversalRenderPipeline.asset;
if (asset)
{
settings.atlas.format = asset.additionalLightsCookieFormat;
settings.atlas.resolution = asset.additionalLightsCookieResolution;
}
p.lightCookieManager = new LightCookieManager(ref settings);
p.clusteredRendering = false;
p.tileSize = 32;
}
return p;
}
}
public ForwardLights() : this(InitParams.GetDefault()) { }
internal ForwardLights(InitParams initParams)
{
if (initParams.clusteredRendering) Assert.IsTrue(math.ispow2(initParams.tileSize));
m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
m_UseClusteredRendering = initParams.clusteredRendering;
LightConstantBuffer._MainLightPosition = Shader.PropertyToID("_MainLightPosition");
LightConstantBuffer._MainLightColor = Shader.PropertyToID("_MainLightColor");
LightConstantBuffer._MainLightOcclusionProbesChannel = Shader.PropertyToID("_MainLightOcclusionProbes");
LightConstantBuffer._MainLightLayerMask = Shader.PropertyToID("_MainLightLayerMask");
LightConstantBuffer._AdditionalLightsCount = Shader.PropertyToID("_AdditionalLightsCount");
if (m_UseStructuredBuffer)
{
m_AdditionalLightsBufferId = Shader.PropertyToID("_AdditionalLightsBuffer");
m_AdditionalLightsIndicesId = Shader.PropertyToID("_AdditionalLightsIndices");
}
else
{
LightConstantBuffer._AdditionalLightsPosition = Shader.PropertyToID("_AdditionalLightsPosition");
LightConstantBuffer._AdditionalLightsColor = Shader.PropertyToID("_AdditionalLightsColor");
LightConstantBuffer._AdditionalLightsAttenuation = Shader.PropertyToID("_AdditionalLightsAttenuation");
LightConstantBuffer._AdditionalLightsSpotDir = Shader.PropertyToID("_AdditionalLightsSpotDir");
LightConstantBuffer._AdditionalLightOcclusionProbeChannel = Shader.PropertyToID("_AdditionalLightsOcclusionProbes");
LightConstantBuffer._AdditionalLightsLayerMasks = Shader.PropertyToID("_AdditionalLightsLayerMasks");
int maxLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
m_AdditionalLightPositions = new Vector4[maxLights];
m_AdditionalLightColors = new Vector4[maxLights];
m_AdditionalLightAttenuations = new Vector4[maxLights];
m_AdditionalLightSpotDirections = new Vector4[maxLights];
m_AdditionalLightOcclusionProbeChannels = new Vector4[maxLights];
m_AdditionalLightsLayerMasks = new float[maxLights];
}
m_LightCookieManager = initParams.lightCookieManager;
if (m_UseClusteredRendering)
{
m_ZBinBuffer = new ComputeBuffer(UniversalRenderPipeline.maxZBins / 4, UnsafeUtility.SizeOf(), ComputeBufferType.Constant, ComputeBufferMode.Dynamic);
m_TileBuffer = new ComputeBuffer(UniversalRenderPipeline.maxTileVec4s, UnsafeUtility.SizeOf(), ComputeBufferType.Constant, ComputeBufferMode.Dynamic);
m_RequestedTileWidth = initParams.tileSize;
}
}
internal void ProcessLights(ref RenderingData renderingData)
{
if (m_UseClusteredRendering)
{
var camera = renderingData.cameraData.camera;
var screenResolution = math.int2(renderingData.cameraData.pixelWidth, renderingData.cameraData.pixelHeight);
var lightCount = renderingData.lightData.visibleLights.Length;
var lightOffset = 0;
while (lightOffset < lightCount && renderingData.lightData.visibleLights[lightOffset].lightType == LightType.Directional)
{
lightOffset++;
}
if (lightOffset == lightCount) lightOffset = 0;
lightCount -= lightOffset;
m_DirectionalLightCount = lightOffset;
if (renderingData.lightData.mainLightIndex != -1) m_DirectionalLightCount -= 1;
var visibleLights = renderingData.lightData.visibleLights.GetSubArray(lightOffset, lightCount);
var lightsPerTile = UniversalRenderPipeline.lightsPerTile;
var wordsPerTile = lightsPerTile / 32;
m_ActualTileWidth = m_RequestedTileWidth >> 1;
do
{
m_ActualTileWidth = m_ActualTileWidth << 1;
m_TileResolution = (screenResolution + m_ActualTileWidth - 1) / m_ActualTileWidth;
}
while ((m_TileResolution.x * m_TileResolution.y * wordsPerTile) > (UniversalRenderPipeline.maxTileVec4s * 4));
var fovHalfHeight = math.tan(math.radians(camera.fieldOfView * 0.5f));
// TODO: Make this work with VR
var fovHalfWidth = fovHalfHeight * (float)screenResolution.x / (float)screenResolution.y;
var maxZFactor = (float)UniversalRenderPipeline.maxZBins / (math.sqrt(camera.farClipPlane) - math.sqrt(camera.nearClipPlane));
m_ZBinFactor = maxZFactor;
m_ZBinOffset = (int)(math.sqrt(camera.nearClipPlane) * m_ZBinFactor);
var binCount = (int)(math.sqrt(camera.farClipPlane) * m_ZBinFactor) - m_ZBinOffset;
// Must be a multiple of 4 to be able to alias to vec4
binCount = ((binCount + 3) / 4) * 4;
binCount = math.min(UniversalRenderPipeline.maxZBins, binCount);
m_ZBins = new NativeArray(binCount, Allocator.TempJob);
Assert.AreEqual(UnsafeUtility.SizeOf(), UnsafeUtility.SizeOf());
using var minMaxZs = new NativeArray(lightCount, Allocator.TempJob);
// We allocate double array length because the sorting algorithm needs swap space to work in.
using var meanZs = new NativeArray(lightCount * 2, Allocator.TempJob);
Matrix4x4 worldToViewMatrix = renderingData.cameraData.GetViewMatrix();
var minMaxZJob = new MinMaxZJob
{
worldToViewMatrix = worldToViewMatrix,
lights = visibleLights,
minMaxZs = minMaxZs,
meanZs = meanZs
};
// Innerloop batch count of 32 is not special, just a handwavy amount to not have too much scheduling overhead nor too little parallelism.
var minMaxZHandle = minMaxZJob.ScheduleParallel(lightCount, 32, new JobHandle());
// We allocate double array length because the sorting algorithm needs swap space to work in.
using var indices = new NativeArray(lightCount * 2, Allocator.TempJob);
var radixSortJob = new RadixSortJob
{
// Floats can be sorted bitwise with no special handling if positive floats only
keys = meanZs.Reinterpret(),
indices = indices
};
var zSortHandle = radixSortJob.Schedule(minMaxZHandle);
var reorderedLights = new NativeArray(lightCount, Allocator.TempJob);
var reorderedMinMaxZs = new NativeArray(lightCount, Allocator.TempJob);
var reorderLightsJob = new ReorderJob { indices = indices, input = visibleLights, output = reorderedLights };
var reorderLightsHandle = reorderLightsJob.ScheduleParallel(lightCount, 32, zSortHandle);
var reorderMinMaxZsJob = new ReorderJob { indices = indices, input = minMaxZs, output = reorderedMinMaxZs };
var reorderMinMaxZsHandle = reorderMinMaxZsJob.ScheduleParallel(lightCount, 32, zSortHandle);
var reorderHandle = JobHandle.CombineDependencies(
reorderLightsHandle,
reorderMinMaxZsHandle
);
JobHandle.ScheduleBatchedJobs();
LightExtractionJob lightExtractionJob;
lightExtractionJob.lights = reorderedLights;
var lightTypes = lightExtractionJob.lightTypes = new NativeArray(lightCount, Allocator.TempJob);
var radiuses = lightExtractionJob.radiuses = new NativeArray(lightCount, Allocator.TempJob);
var directions = lightExtractionJob.directions = new NativeArray(lightCount, Allocator.TempJob);
var positions = lightExtractionJob.positions = new NativeArray(lightCount, Allocator.TempJob);
var coneRadiuses = lightExtractionJob.coneRadiuses = new NativeArray(lightCount, Allocator.TempJob);
var lightExtractionHandle = lightExtractionJob.ScheduleParallel(lightCount, 32, reorderHandle);
var zBinningJob = new ZBinningJob
{
bins = m_ZBins,
minMaxZs = reorderedMinMaxZs,
binOffset = m_ZBinOffset,
zFactor = m_ZBinFactor
};
var zBinningHandle = zBinningJob.ScheduleParallel((binCount + ZBinningJob.batchCount - 1) / ZBinningJob.batchCount, 1, reorderHandle);
reorderedMinMaxZs.Dispose(zBinningHandle);
// Must be a multiple of 4 to be able to alias to vec4
var lightMasksLength = (((wordsPerTile) * m_TileResolution + 3) / 4) * 4;
var horizontalLightMasks = new NativeArray(lightMasksLength.y, Allocator.TempJob);
var verticalLightMasks = new NativeArray(lightMasksLength.x, Allocator.TempJob);
// Vertical slices along the x-axis
var verticalJob = new SliceCullingJob
{
scale = (float)m_ActualTileWidth / (float)screenResolution.x,
viewOrigin = camera.transform.position,
viewForward = camera.transform.forward,
viewRight = camera.transform.right * fovHalfWidth,
viewUp = camera.transform.up * fovHalfHeight,
lightTypes = lightTypes,
radiuses = radiuses,
directions = directions,
positions = positions,
coneRadiuses = coneRadiuses,
lightsPerTile = lightsPerTile,
sliceLightMasks = verticalLightMasks
};
var verticalHandle = verticalJob.ScheduleParallel(m_TileResolution.x, 1, lightExtractionHandle);
// Horizontal slices along the y-axis
var horizontalJob = verticalJob;
horizontalJob.scale = (float)m_ActualTileWidth / (float)screenResolution.y;
horizontalJob.viewRight = camera.transform.up * fovHalfHeight;
horizontalJob.viewUp = -camera.transform.right * fovHalfWidth;
horizontalJob.sliceLightMasks = horizontalLightMasks;
var horizontalHandle = horizontalJob.ScheduleParallel(m_TileResolution.y, 1, lightExtractionHandle);
var slicesHandle = JobHandle.CombineDependencies(horizontalHandle, verticalHandle);
m_TileLightMasks = new NativeArray(((m_TileResolution.x * m_TileResolution.y * (wordsPerTile) + 3) / 4) * 4, Allocator.TempJob);
var sliceCombineJob = new SliceCombineJob
{
tileResolution = m_TileResolution,
wordsPerTile = wordsPerTile,
sliceLightMasksH = horizontalLightMasks,
sliceLightMasksV = verticalLightMasks,
lightMasks = m_TileLightMasks
};
var sliceCombineHandle = sliceCombineJob.ScheduleParallel(m_TileResolution.y, 1, slicesHandle);
m_CullingHandle = JobHandle.CombineDependencies(sliceCombineHandle, zBinningHandle);
reorderHandle.Complete();
NativeArray.Copy(reorderedLights, 0, renderingData.lightData.visibleLights, lightOffset, lightCount);
var tempBias = new NativeArray(lightCount, Allocator.Temp);
var tempResolution = new NativeArray(lightCount, Allocator.Temp);
var tempIndices = new NativeArray(lightCount, Allocator.Temp);
for (var i = 0; i < lightCount; i++)
{
tempBias[indices[i]] = renderingData.shadowData.bias[lightOffset + i];
tempResolution[indices[i]] = renderingData.shadowData.resolution[lightOffset + i];
tempIndices[indices[i]] = lightOffset + i;
}
for (var i = 0; i < lightCount; i++)
{
renderingData.shadowData.bias[i + lightOffset] = tempBias[i];
renderingData.shadowData.resolution[i + lightOffset] = tempResolution[i];
renderingData.lightData.originalIndices[i + lightOffset] = tempIndices[i];
}
tempBias.Dispose();
tempResolution.Dispose();
tempIndices.Dispose();
lightTypes.Dispose(m_CullingHandle);
radiuses.Dispose(m_CullingHandle);
directions.Dispose(m_CullingHandle);
positions.Dispose(m_CullingHandle);
coneRadiuses.Dispose(m_CullingHandle);
reorderedLights.Dispose(m_CullingHandle);
horizontalLightMasks.Dispose(m_CullingHandle);
verticalLightMasks.Dispose(m_CullingHandle);
JobHandle.ScheduleBatchedJobs();
}
}
public void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
{
int additionalLightsCount = renderingData.lightData.additionalLightsCount;
bool additionalLightsPerVertex = renderingData.lightData.shadeAdditionalLightsPerVertex;
CommandBuffer cmd = CommandBufferPool.Get();
using (new ProfilingScope(null, m_ProfilingSampler))
{
var useClusteredRendering = m_UseClusteredRendering;
if (useClusteredRendering)
{
m_CullingHandle.Complete();
m_ZBinBuffer.SetData(m_ZBins.Reinterpret(UnsafeUtility.SizeOf()), 0, 0, m_ZBins.Length / 4);
m_TileBuffer.SetData(m_TileLightMasks.Reinterpret(UnsafeUtility.SizeOf()), 0, 0, m_TileLightMasks.Length / 4);
cmd.SetGlobalInteger("_AdditionalLightsDirectionalCount", m_DirectionalLightCount);
cmd.SetGlobalInteger("_AdditionalLightsZBinOffset", m_ZBinOffset);
cmd.SetGlobalFloat("_AdditionalLightsZBinScale", m_ZBinFactor);
cmd.SetGlobalVector("_AdditionalLightsTileScale", renderingData.cameraData.pixelRect.size / (float)m_ActualTileWidth);
cmd.SetGlobalInteger("_AdditionalLightsTileCountX", m_TileResolution.x);
cmd.SetGlobalConstantBuffer(m_ZBinBuffer, "AdditionalLightsZBins", 0, m_ZBins.Length * 4);
cmd.SetGlobalConstantBuffer(m_TileBuffer, "AdditionalLightsTiles", 0, m_TileLightMasks.Length * 4);
m_ZBins.Dispose();
m_TileLightMasks.Dispose();
}
SetupShaderLightConstants(cmd, ref renderingData);
bool lightCountCheck = (renderingData.cameraData.renderer.stripAdditionalLightOffVariants && renderingData.lightData.supportsAdditionalLights) || additionalLightsCount > 0;
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsVertex,
lightCountCheck && additionalLightsPerVertex && !useClusteredRendering);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightsPixel,
lightCountCheck && !additionalLightsPerVertex && !useClusteredRendering);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ClusteredRendering,
useClusteredRendering);
bool isShadowMask = renderingData.lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.ShadowMask;
bool isShadowMaskAlways = isShadowMask && QualitySettings.shadowmaskMode == ShadowmaskMode.Shadowmask;
bool isSubtractive = renderingData.lightData.supportsMixedLighting && m_MixedLightingSetup == MixedLightingSetup.Subtractive;
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.LightmapShadowMixing, isSubtractive || isShadowMaskAlways);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ShadowsShadowMask, isShadowMask);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MixedLightingSubtractive, isSubtractive); // Backward compatibility
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ReflectionProbeBlending, renderingData.lightData.reflectionProbeBlending);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.ReflectionProbeBoxProjection, renderingData.lightData.reflectionProbeBoxProjection);
bool lightLayers = renderingData.lightData.supportsLightLayers;
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.LightLayers, lightLayers);
m_LightCookieManager.Setup(context, cmd, ref renderingData.lightData);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
internal void Cleanup()
{
if (m_UseClusteredRendering)
{
m_ZBinBuffer.Dispose();
m_TileBuffer.Dispose();
}
}
void InitializeLightConstants(NativeArray lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightAttenuation, out Vector4 lightSpotDir, out Vector4 lightOcclusionProbeChannel, out uint lightLayerMask)
{
UniversalRenderPipeline.InitializeLightConstants_Common(lights, lightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionProbeChannel);
lightLayerMask = 0;
// When no lights are visible, main light will be set to -1.
// In this case we initialize it to default values and return
if (lightIndex < 0)
return;
VisibleLight lightData = lights[lightIndex];
Light light = lightData.light;
if (light == null)
return;
if (light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed &&
lightData.light.shadows != LightShadows.None &&
m_MixedLightingSetup == MixedLightingSetup.None)
{
switch (light.bakingOutput.mixedLightingMode)
{
case MixedLightingMode.Subtractive:
m_MixedLightingSetup = MixedLightingSetup.Subtractive;
break;
case MixedLightingMode.Shadowmask:
m_MixedLightingSetup = MixedLightingSetup.ShadowMask;
break;
}
}
var additionalLightData = light.GetUniversalAdditionalLightData();
lightLayerMask = (uint)additionalLightData.lightLayerMask;
}
void SetupShaderLightConstants(CommandBuffer cmd, ref RenderingData renderingData)
{
m_MixedLightingSetup = MixedLightingSetup.None;
// Main light has an optimized shader path for main light. This will benefit games that only care about a single light.
// Universal pipeline also supports only a single shadow light, if available it will be the main light.
SetupMainLightConstants(cmd, ref renderingData.lightData);
SetupAdditionalLightConstants(cmd, ref renderingData);
}
void SetupMainLightConstants(CommandBuffer cmd, ref LightData lightData)
{
Vector4 lightPos, lightColor, lightAttenuation, lightSpotDir, lightOcclusionChannel;
uint lightLayerMask;
InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, out lightPos, out lightColor, out lightAttenuation, out lightSpotDir, out lightOcclusionChannel, out lightLayerMask);
cmd.SetGlobalVector(LightConstantBuffer._MainLightPosition, lightPos);
cmd.SetGlobalVector(LightConstantBuffer._MainLightColor, lightColor);
cmd.SetGlobalVector(LightConstantBuffer._MainLightOcclusionProbesChannel, lightOcclusionChannel);
cmd.SetGlobalInt(LightConstantBuffer._MainLightLayerMask, (int)lightLayerMask);
}
void SetupAdditionalLightConstants(CommandBuffer cmd, ref RenderingData renderingData)
{
ref LightData lightData = ref renderingData.lightData;
var cullResults = renderingData.cullResults;
var lights = lightData.visibleLights;
int maxAdditionalLightsCount = UniversalRenderPipeline.maxVisibleAdditionalLights;
int additionalLightsCount = SetupPerObjectLightIndices(cullResults, ref lightData);
if (additionalLightsCount > 0)
{
if (m_UseStructuredBuffer)
{
NativeArray additionalLightsData = new NativeArray(additionalLightsCount, Allocator.Temp);
for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
{
VisibleLight light = lights[i];
if (lightData.mainLightIndex != i)
{
ShaderInput.LightData data;
InitializeLightConstants(lights, i,
out data.position, out data.color, out data.attenuation,
out data.spotDirection, out data.occlusionProbeChannels,
out data.layerMask);
additionalLightsData[lightIter] = data;
lightIter++;
}
}
var lightDataBuffer = ShaderData.instance.GetLightDataBuffer(additionalLightsCount);
lightDataBuffer.SetData(additionalLightsData);
int lightIndices = cullResults.lightAndReflectionProbeIndexCount;
var lightIndicesBuffer = ShaderData.instance.GetLightIndicesBuffer(lightIndices);
cmd.SetGlobalBuffer(m_AdditionalLightsBufferId, lightDataBuffer);
cmd.SetGlobalBuffer(m_AdditionalLightsIndicesId, lightIndicesBuffer);
additionalLightsData.Dispose();
}
else
{
for (int i = 0, lightIter = 0; i < lights.Length && lightIter < maxAdditionalLightsCount; ++i)
{
VisibleLight light = lights[i];
if (lightData.mainLightIndex != i)
{
uint lightLayerMask;
InitializeLightConstants(lights, i, out m_AdditionalLightPositions[lightIter],
out m_AdditionalLightColors[lightIter],
out m_AdditionalLightAttenuations[lightIter],
out m_AdditionalLightSpotDirections[lightIter],
out m_AdditionalLightOcclusionProbeChannels[lightIter],
out lightLayerMask);
m_AdditionalLightsLayerMasks[lightIter] = Unity.Mathematics.math.asfloat(lightLayerMask);
lightIter++;
}
}
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsPosition, m_AdditionalLightPositions);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsColor, m_AdditionalLightColors);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsAttenuation, m_AdditionalLightAttenuations);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsSpotDir, m_AdditionalLightSpotDirections);
cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightOcclusionProbeChannel, m_AdditionalLightOcclusionProbeChannels);
cmd.SetGlobalFloatArray(LightConstantBuffer._AdditionalLightsLayerMasks, m_AdditionalLightsLayerMasks);
}
cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, new Vector4(lightData.maxPerObjectAdditionalLightsCount,
0.0f, 0.0f, 0.0f));
}
else
{
cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, Vector4.zero);
}
}
int SetupPerObjectLightIndices(CullingResults cullResults, ref LightData lightData)
{
if (lightData.additionalLightsCount == 0)
return lightData.additionalLightsCount;
var visibleLights = lightData.visibleLights;
var perObjectLightIndexMap = cullResults.GetLightIndexMap(Allocator.Temp);
int globalDirectionalLightsCount = 0;
int additionalLightsCount = 0;
// Disable all directional lights from the perobject light indices
// Pipeline handles main light globally and there's no support for additional directional lights atm.
for (int i = 0; i < visibleLights.Length; ++i)
{
if (additionalLightsCount >= UniversalRenderPipeline.maxVisibleAdditionalLights)
break;
VisibleLight light = visibleLights[i];
if (i == lightData.mainLightIndex)
{
perObjectLightIndexMap[i] = -1;
++globalDirectionalLightsCount;
}
else
{
perObjectLightIndexMap[i] -= globalDirectionalLightsCount;
++additionalLightsCount;
}
}
// Disable all remaining lights we cannot fit into the global light buffer.
for (int i = globalDirectionalLightsCount + additionalLightsCount; i < perObjectLightIndexMap.Length; ++i)
perObjectLightIndexMap[i] = -1;
cullResults.SetLightIndexMap(perObjectLightIndexMap);
if (m_UseStructuredBuffer && additionalLightsCount > 0)
{
int lightAndReflectionProbeIndices = cullResults.lightAndReflectionProbeIndexCount;
Assertions.Assert.IsTrue(lightAndReflectionProbeIndices > 0, "Pipelines configures additional lights but per-object light and probe indices count is zero.");
cullResults.FillLightAndReflectionProbeIndices(ShaderData.instance.GetLightIndicesBuffer(lightAndReflectionProbeIndices));
}
perObjectLightIndexMap.Dispose();
return additionalLightsCount;
}
}
}