浏览代码

Light sorting added based on lightType, shadowType, cookie support, intensity if directional, distance to camera otherwise.

/Add-support-for-light-specular-color-tint
Felipe Lira 7 年前
当前提交
252443ec
共有 2 个文件被更改,包括 128 次插入62 次删除
  1. 150
      ScriptableRenderPipeline/LightweightPipeline/LightweightPipeline.cs
  2. 40
      ScriptableRenderPipeline/LightweightPipeline/LightweightPipelineUtils.cs

150
ScriptableRenderPipeline/LightweightPipeline/LightweightPipeline.cs


using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.XR;

// Max amount of visible lights. This controls the lights constant buffers in shader but not the max shaded lights.
// Lights are set per-object and the max shaded lights for each object are controlled by the max pixel lights in pipeline asset and kMaxVertexLights.
private static readonly int kMaxVisibleLights = 16;
private static readonly int kMaxVisibleAdditionalLights = 16;
private Vector4[] m_LightPositions = new Vector4[kMaxVisibleLights];
private Vector4[] m_LightColors = new Vector4[kMaxVisibleLights];
private Vector4[] m_LightAttenuations = new Vector4[kMaxVisibleLights];
private Vector4[] m_LightSpotDirections = new Vector4[kMaxVisibleLights];
private Vector4[] m_LightPositions = new Vector4[kMaxVisibleAdditionalLights];
private Vector4[] m_LightColors = new Vector4[kMaxVisibleAdditionalLights];
private Vector4[] m_LightAttenuations = new Vector4[kMaxVisibleAdditionalLights];
private Vector4[] m_LightSpotDirections = new Vector4[kMaxVisibleAdditionalLights];
private Camera m_CurrCamera = null;

private PostProcessLayer m_CameraPostProcessLayer;
private CameraComparer m_CameraComparer = new CameraComparer();
private LightComparer m_LightCompararer = new LightComparer();
// Maps from sorted light indices to original unsorted. We need this for shadow rendering
// and per-object light lists.
private List<int> m_SortedLightIndexMap = new List<int>();
private Mesh m_BlitQuad;
private Material m_BlitMaterial;

LightData lightData;
InitializeLightData(visibleLights, out lightData);
ShadowPass(visibleLights, ref context, ref lightData);
ForwardPass(visibleLights, ref context, ref lightData, stereoEnabled);

{
VisibleLight mainLight = visibleLights[lightData.mainLightIndex];
if (mainLight.light.shadows != LightShadows.None)
lightData.shadowsRendered = RenderShadows (ref m_CullResults,
ref mainLight, lightData.mainLightIndex, ref context);
{
// There's no way to map shadow light indices. We need to pass in the original unsorted index.
// If no additional lights then no light sorting is performed and the indices match.
int shadowOriginalIndex = (lightData.hasAdditionalLights) ? GetLightUnsortedIndex(lightData.mainLightIndex) : lightData.mainLightIndex;
lightData.shadowsRendered = RenderShadows(ref m_CullResults, ref mainLight,
shadowOriginalIndex, ref context);
}
}
}

else
m_IntermediateTextureArray = false;
bool intermediateTexture = m_CurrCamera.targetTexture != null || m_CurrCamera.cameraType == CameraType.SceneView ||
bool intermediateTexture = m_CurrCamera.targetTexture != null || m_CurrCamera.cameraType == CameraType.SceneView ||
m_ColorFormat = m_CurrCamera.allowHDR ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32;
m_CameraPostProcessLayer = m_CurrCamera.GetComponent<PostProcessLayer>();

private void InitializeLightData(VisibleLight[] visibleLights, out LightData lightData)
{
int visibleLightsCount = visibleLights.Length;
m_SortedLightIndexMap.Clear();
// kMaxPerObjectLights + 1 main light
int maxSupportedPixelLights = Math.Min(m_Asset.MaxSupportedPixelLights, kMaxPerObjectLights + 1);

}
else
{
lightData.mainLightIndex = GetMainLightIndex(visibleLights);
lightData.mainLightIndex = SortLights(visibleLights);
// How main light is decided:
// 1) If shadows are enabled and at least a shadow light is present:
// Main light is a shadow light. Directional shadow light has priority over the other supported
// shadow lights types.
// Lightweight pipeline only supports 1 single directional shadow light.
// Any additional pixel lights don't cast shadows.
//
// 2) If shadows are disabled or no shadow light is present then main light is the main brighest directional
// 3) If neither a shadow light or main light is first visible light.
private int GetMainLightIndex(VisibleLight[] lights)
private int SortLights(VisibleLight[] visibleLights)
bool shadowsEnabled = m_Asset.AreShadowsEnabled();
int mainDirectional = -1;
int mainShadowLight = -1;
int totalVisibleLights = visibleLights.Length;
for (int i = 0; i < lights.Length; ++i)
{
if (shadowsEnabled && LightweightUtils.IsSupportedShadowType (lights[i].lightType) && lights[i].light.shadows != LightShadows.None)
{
// Shadow Type priority: Soft > Hard > None
if (mainShadowLight < 0 || lights[i].light.shadows > lights[mainShadowLight].light.shadows)
mainShadowLight = i;
}
Dictionary<int, int> visibleLightsIDMap = new Dictionary<int, int>();
for (int i = 0; i < totalVisibleLights; ++i)
visibleLightsIDMap.Add(visibleLights[i].light.GetInstanceID(), i);
if (lights[i].lightType == LightType.Directional)
{
if (mainDirectional < 0 || lights[i].light.intensity > lights[mainDirectional].light.intensity)
mainDirectional = i;
}
}
// Sorts light so we have all directionals first, then local lights.
// Directionals are sorted further by shadow, cookie and intensity
// Locals are sorted further by shadow, cookie and distance to camera
m_LightCompararer.CurrCamera = m_CurrCamera;
Array.Sort(visibleLights, m_LightCompararer);
if (mainShadowLight >= 0)
{
if (mainDirectional > 0 && lights[mainDirectional].light.shadows != LightShadows.None)
return mainDirectional;
else
return mainShadowLight;
}
for (int i = 0; i < totalVisibleLights; ++i)
m_SortedLightIndexMap.Add(visibleLightsIDMap[visibleLights[i].light.GetInstanceID()]);
return GetMainLight(visibleLights);
}
// How main light is decided:
// If shadows enabled, main light is always a shadow casting light. Directional has priority over local lights.
// Otherwise directional lights have priority based on cookie support and intensity
// If no directional light in the scene local lights based on cookie support and distance to camera
private int GetMainLight(VisibleLight[] visibleLights)
{
int totalVisibleLights = visibleLights.Length;
bool shadowsEnabled = m_Asset.AreShadowsEnabled();
// If shadows are supported and the first visible light has shadows then this is main light
if (shadowsEnabled && visibleLights[0].light.shadows != LightShadows.None)
return 0;
if (mainDirectional > 0)
return mainDirectional;
// We don't have any directional shadow casting light, skip until we find the first non directional light
int lightIndex = 0;
while (lightIndex < totalVisibleLights && visibleLights[lightIndex].lightType == LightType.Directional)
lightIndex++;
return 0;
// If first non-directional light has shadows we return it, otherwise we return first light
return (lightIndex < totalVisibleLights && visibleLights[lightIndex].light.shadows != LightShadows.None) ? lightIndex : 0;
}
private void InitializeLightConstants(VisibleLight[] lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightSpotDir,

private void SetupAdditionalListConstants(CommandBuffer cmd, VisibleLight[] lights, ref LightData lightData, ref ScriptableRenderContext context)
{
int totalLightCount = lights.Length;
int maxLights = Math.Min(kMaxVisibleLights, totalLightCount);
int additionalLightIndex = 0;
// We need to update per-object light list with the proper map to our global additional light buffer
// First we initialize all lights in the map to -1 to tell the system to discard main light index and
// remaining lights in the scene that don't fit the max additional light buffer (kMaxVisibileAdditionalLights)
int[] perObjectLightIndexMap = m_CullResults.GetLightIndexMap();
for (int i = 0; i < lights.Length; ++i)
perObjectLightIndexMap[i] = -1;
int[] lightIndexMap = m_CullResults.GetLightIndexMap();
int lightIter = 0;
for (int i = 0; i < totalLightCount; ++i)
for (int i = 0; i < lights.Length && additionalLightIndex < kMaxVisibleAdditionalLights; ++i)
if (i == lightData.mainLightIndex || lightIter >= maxLights)
if (i != lightData.mainLightIndex)
lightIndexMap[i] = -1;
continue;
// The engine performs per-object light culling and initialize 8 light indices into two vec4 constants unity_4LightIndices0 and unity_4LightIndices1.
// In the shader we iterate over each visible light using the indices provided in these constants to index our global light buffer
// ex: first light position would be m_LightPosisitions[unity_4LightIndices[0]];
// However since we sorted the lights we need to tell the engine how to map the original/unsorted indices to our global buffer
// We do it by settings the perObjectLightIndexMap to the appropriate additionalLightIndex.
perObjectLightIndexMap[GetLightUnsortedIndex(i)] = additionalLightIndex;
InitializeLightConstants(lights, i, out m_LightPositions[additionalLightIndex],
out m_LightColors[additionalLightIndex],
out m_LightSpotDirections[additionalLightIndex],
out m_LightAttenuations[additionalLightIndex]);
additionalLightIndex++;
InitializeLightConstants(lights, i, out m_LightPositions[lightIter], out m_LightColors[lightIter], out m_LightSpotDirections[lightIter], out m_LightAttenuations[lightIter]);
lightIndexMap[i] = lightIter;
lightIter++;
m_CullResults.SetLightIndexMap(lightIndexMap);
m_CullResults.SetLightIndexMap(perObjectLightIndexMap);
cmd.SetGlobalVector(PerCameraBuffer._AdditionalLightCount, new Vector4 (lightData.additionalPixelLightsCount, lightData.vertexLightsCount, 0.0f, 0.0f));
cmd.SetGlobalVectorArray (PerCameraBuffer._AdditionalLightPosition, m_LightPositions);

{
if (m_CurrCamera.targetTexture == null)
colorRT = m_CameraColorRT;
if (LightweightUtils.HasFlag(renderingConfig, FrameRenderingConfiguration.RequireDepth))
depthRT = m_CameraDepthRT;
}

m_CameraPostProcessLayer.RenderOpaqueOnly(m_PostProcessRenderContext);
else
m_CameraPostProcessLayer.Render(m_PostProcessRenderContext);
}
private int GetLightUnsortedIndex(int index)
{
Debug.Assert(index >= 0 && index < m_SortedLightIndexMap.Count, "Invalid index while accessing light index map. If you only have a single light in scene you should not try to map indices");
return m_SortedLightIndexMap[index];
}
private void Blit(CommandBuffer cmd, FrameRenderingConfiguration renderingConfig, RenderTargetIdentifier sourceRT, RenderTargetIdentifier destRT, Material material = null)

40
ScriptableRenderPipeline/LightweightPipeline/LightweightPipelineUtils.cs


}
}
public class LightComparer : IComparer<VisibleLight>
{
public Camera CurrCamera { get; set; }
// Sorts on the following priority:
// Directionals have priority over local lights
// ShadowLight type
// Has Cookie
// Intensity if Directional, Distance to camera otherwise
public int Compare(VisibleLight lhs, VisibleLight rhs)
{
Light lhsLight = lhs.light;
Light rhsLight = rhs.light;
if (lhs.lightType != rhs.lightType)
{
if (lhs.lightType == LightType.Directional) return -1;
if (rhs.lightType == LightType.Directional) return 1;
}
// In the following priority: Soft, Hard, None
if (lhsLight.shadows != rhsLight.shadows)
return (int)rhsLight.shadows - (int)lhsLight.shadows;
if (lhsLight.cookie != rhsLight.cookie)
return (lhsLight.cookie != null) ? -1 : 1;
if (lhs.lightType == LightType.Directional)
return (int)(lhsLight.intensity*100.0f) - (int)(rhsLight.intensity*100.0f);
else
return (int)(SquaredDistanceToCamera(lhsLight.transform.position) - SquaredDistanceToCamera(rhsLight.transform.position));
}
public float SquaredDistanceToCamera(Vector3 lightPos)
{
Vector3 lightCameraVector = lightPos - CurrCamera.transform.position;
return Vector3.Dot(lightCameraVector, lightCameraVector);
}
}
[Flags]
public enum FrameRenderingConfiguration
{

正在加载...
取消
保存