|
|
|
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using UnityEngine.Rendering; |
|
|
|
|
|
|
|
namespace UnityEngine.Experimental.Rendering.LightweightPipeline |
|
|
|
|
|
|
public int shadowResolution; |
|
|
|
} |
|
|
|
|
|
|
|
public struct LightData |
|
|
|
{ |
|
|
|
public int pixelLightsCount; |
|
|
|
public int vertexLightsCount; |
|
|
|
public bool isSingleDirectionalLight; |
|
|
|
} |
|
|
|
|
|
|
|
public class LightweightPipeline : RenderPipeline |
|
|
|
{ |
|
|
|
private readonly LightweightPipelineAsset m_Asset; |
|
|
|
|
|
|
private Vector4[] m_LightAttenuations = new Vector4[kMaxVisibleLights]; |
|
|
|
private Vector4[] m_LightSpotDirections = new Vector4[kMaxVisibleLights]; |
|
|
|
|
|
|
|
// Amount of light indices buffer set per object.
|
|
|
|
// TODO: Change cullresults to return amount of renderers so we can allocate/reallocate enough buffer data
|
|
|
|
// As off now allocating a enough buffer to hold a scene that should enough for a small demo/game
|
|
|
|
private static readonly int kMaxLightIndices = 1024 * kMaxVisibleLights; |
|
|
|
private int m_LightIndicesCount = 0; |
|
|
|
private ComputeBuffer m_LightIndexListBuffer; |
|
|
|
|
|
|
|
private static readonly int kMaxCascades = 4; |
|
|
|
|
|
|
private ShadowSettings m_ShadowSettings = ShadowSettings.Default; |
|
|
|
private ShadowSliceData[] m_ShadowSlices = new ShadowSliceData[kMaxCascades]; |
|
|
|
|
|
|
|
private static readonly ShaderPassName m_ForwardBasePassName = new ShaderPassName("LightweightForward") ; |
|
|
|
|
|
|
|
private static readonly ShaderPassName m_LitPassName = new ShaderPassName("LightweightForward"); |
|
|
|
private static readonly ShaderPassName m_UnlitPassName = new ShaderPassName("SrpDefaultUnlit"); |
|
|
|
|
|
|
|
public LightweightPipeline(LightweightPipelineAsset asset) |
|
|
|
{ |
|
|
|
m_Asset = asset; |
|
|
|
|
|
|
m_ShadowMapRTID = new RenderTargetIdentifier(m_ShadowMapProperty); |
|
|
|
Shader.globalRenderPipeline = "LightweightPipeline"; |
|
|
|
|
|
|
|
// TODO: Change cullresults to return amount of renderers so we can allocate/reallocate enough buffer data
|
|
|
|
m_LightIndexListBuffer = new ComputeBuffer(kMaxLightIndices, sizeof(uint)); |
|
|
|
m_LightIndexListBuffer.Dispose(); |
|
|
|
if (m_LightIndexListBuffer != null) |
|
|
|
{ |
|
|
|
m_LightIndexListBuffer.Dispose(); |
|
|
|
m_LightIndexListBuffer = null; |
|
|
|
m_LightIndicesCount = 0; |
|
|
|
} |
|
|
|
base.Render(context, cameras); |
|
|
|
base.Render(context, cameras); |
|
|
|
|
|
|
|
foreach (Camera camera in cameras) |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
cullingParameters.shadowDistance = m_ShadowSettings.maxShadowDistance; |
|
|
|
CullResults cull = CullResults.Cull(ref cullingParameters, context); |
|
|
|
CullResults cullResults = CullResults.Cull(ref cullingParameters, context); |
|
|
|
VisibleLight[] visibleLights = cull.visibleLights; |
|
|
|
VisibleLight[] visibleLights = cullResults.visibleLights; |
|
|
|
int pixelLightsCount, vertexLightsCount; |
|
|
|
GetMaxSupportedLights(visibleLights.Length, out pixelLightsCount, out vertexLightsCount); |
|
|
|
|
|
|
|
// TODO: Add remaining lights to SH
|
|
|
|
LightData lightData; |
|
|
|
InitializeLightData(visibleLights, out lightData); |
|
|
|
shadowsRendered = RenderShadows(cull, visibleLights[m_ShadowLightIndex], context); |
|
|
|
shadowsRendered = RenderShadows(ref cullResults, ref visibleLights[m_ShadowLightIndex], ref context); |
|
|
|
|
|
|
|
// Setup camera matrices and RT
|
|
|
|
context.SetupCameraProperties(camera); |
|
|
|
|
|
|
cmd.ClearRenderTarget(true, true, camera.backgroundColor); |
|
|
|
context.ExecuteCommandBuffer(cmd); |
|
|
|
cmd.Dispose(); |
|
|
|
|
|
|
|
if (lightData.isSingleDirectionalLight) |
|
|
|
FillLightIndices(ref cullResults, visibleLights.Length); |
|
|
|
cull.FillLightIndices(m_LightIndexListBuffer); |
|
|
|
SetupLightShaderVariables(visibleLights, pixelLightsCount, vertexLightsCount, context); |
|
|
|
SetupLightShaderVariables(visibleLights, ref cullResults, ref context, ref lightData); |
|
|
|
SetupShadowShaderVariables(context, m_ShadowCasterCascadesCount); |
|
|
|
SetupShadowShaderVariables(ref context, m_ShadowCasterCascadesCount); |
|
|
|
// Render Opaques
|
|
|
|
var settings = new DrawRendererSettings(cull, camera, m_ForwardBasePassName); |
|
|
|
settings.sorting.flags = SortFlags.CommonOpaque; |
|
|
|
settings.inputFilter.SetQueuesOpaque(); |
|
|
|
|
|
|
|
settings.rendererConfiguration = RendererConfiguration.PerObjectReflectionProbes; |
|
|
|
RendererConfiguration configuration = RendererConfiguration.PerObjectReflectionProbes; |
|
|
|
settings.rendererConfiguration |= RendererConfiguration.PerObjectLightmaps; |
|
|
|
configuration |= RendererConfiguration.PerObjectLightmaps; |
|
|
|
settings.rendererConfiguration |= RendererConfiguration.PerObjectLightProbe; |
|
|
|
configuration |= RendererConfiguration.PerObjectLightProbe; |
|
|
|
settings.rendererConfiguration |= RendererConfiguration.ProvideLightIndices; |
|
|
|
if (!lightData.isSingleDirectionalLight) |
|
|
|
configuration |= RendererConfiguration.ProvideLightIndices; |
|
|
|
context.DrawRenderers(ref settings); |
|
|
|
// Render Opaques
|
|
|
|
var litSettings = new DrawRendererSettings(cullResults, camera, m_LitPassName); |
|
|
|
litSettings.sorting.flags = SortFlags.CommonOpaque; |
|
|
|
litSettings.inputFilter.SetQueuesOpaque(); |
|
|
|
litSettings.rendererConfiguration = configuration; |
|
|
|
|
|
|
|
var unlitSettings = new DrawRendererSettings(cullResults, camera, m_UnlitPassName); |
|
|
|
unlitSettings.sorting.flags = SortFlags.CommonOpaque; |
|
|
|
unlitSettings.inputFilter.SetQueuesOpaque(); |
|
|
|
|
|
|
|
context.DrawRenderers(ref litSettings); |
|
|
|
|
|
|
|
// Release temporary RT
|
|
|
|
var discardRT = new CommandBuffer(); |
|
|
|
|
|
|
|
|
|
|
context.DrawRenderers(ref unlitSettings); |
|
|
|
|
|
|
|
settings.sorting.flags = SortFlags.CommonTransparent; |
|
|
|
settings.inputFilter.SetQueuesTransparent(); |
|
|
|
context.DrawRenderers(ref settings); |
|
|
|
litSettings.sorting.flags = SortFlags.CommonTransparent; |
|
|
|
litSettings.inputFilter.SetQueuesTransparent(); |
|
|
|
context.DrawRenderers(ref litSettings); |
|
|
|
|
|
|
|
unlitSettings.sorting.flags = SortFlags.CommonTransparent; |
|
|
|
unlitSettings.inputFilter.SetQueuesTransparent(); |
|
|
|
context.DrawRenderers(ref unlitSettings); |
|
|
|
} |
|
|
|
|
|
|
|
context.Submit(); |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void GetMaxSupportedLights(int lightsCount, out int pixelLightsCount, out int vertexLightsCount) |
|
|
|
{ |
|
|
|
pixelLightsCount = Mathf.Min(lightsCount, m_Asset.MaxSupportedPixelLights); |
|
|
|
vertexLightsCount = (m_Asset.SupportsVertexLight) ? Mathf.Min(lightsCount - pixelLightsCount, kMaxVertexLights) : 0; |
|
|
|
} |
|
|
|
|
|
|
|
private void InitializeLightData() |
|
|
|
private void InitializeLightData(VisibleLight[] lights, out LightData lightData) |
|
|
|
{ |
|
|
|
for (int i = 0; i < kMaxVisibleLights; ++i) |
|
|
|
{ |
|
|
|
|
|
|
m_LightSpotDirections[i] = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); |
|
|
|
} |
|
|
|
|
|
|
|
int lightsCount = lights.Length; |
|
|
|
lightData.pixelLightsCount = Mathf.Min(lightsCount, m_Asset.MaxSupportedPixelLights); |
|
|
|
lightData.vertexLightsCount = (m_Asset.SupportsVertexLight) ? Mathf.Min(lightsCount - lightData.pixelLightsCount, kMaxVertexLights) : 0; |
|
|
|
lightData.isSingleDirectionalLight = lightData.pixelLightsCount == 1 && lightData.vertexLightsCount == 0 && lights[0].lightType == LightType.Directional; |
|
|
|
private void SetupLightShaderVariables(VisibleLight[] lights, int pixelLightCount, int vertexLightCount, ScriptableRenderContext context) |
|
|
|
private void FillLightIndices(ref CullResults cullResults, int visibleLightsCount) |
|
|
|
{ |
|
|
|
int visibleRenderersCount = cullResults.GetVisibleRenderersCount(); |
|
|
|
if (visibleRenderersCount > m_LightIndicesCount) |
|
|
|
{ |
|
|
|
m_LightIndicesCount = visibleRenderersCount * visibleLightsCount; |
|
|
|
if (m_LightIndexListBuffer != null) |
|
|
|
m_LightIndexListBuffer.Release(); |
|
|
|
m_LightIndexListBuffer = new ComputeBuffer(m_LightIndicesCount, sizeof(uint)); |
|
|
|
} |
|
|
|
cullResults.FillLightIndices(m_LightIndexListBuffer); |
|
|
|
} |
|
|
|
|
|
|
|
private void SetupLightShaderVariables(VisibleLight[] lights, ref CullResults cullResults, ref ScriptableRenderContext context, ref LightData lightData) |
|
|
|
InitializeLightData(); |
|
|
|
int maxLights = 1; |
|
|
|
if (!lightData.isSingleDirectionalLight) |
|
|
|
{ |
|
|
|
FillLightIndices(ref cullResults, lights.Length); |
|
|
|
Math.Min(kMaxVisibleLights, lights.Length); |
|
|
|
} |
|
|
|
int maxLights = Math.Min(kMaxVisibleLights, lights.Length); |
|
|
|
for (int i = 0; i < maxLights; ++i) |
|
|
|
{ |
|
|
|
VisibleLight currLight = lights[i]; |
|
|
|
|
|
|
cmd.SetGlobalVectorArray("globalLightColor", m_LightColors); |
|
|
|
cmd.SetGlobalVectorArray("globalLightAtten", m_LightAttenuations); |
|
|
|
cmd.SetGlobalVectorArray("globalLightSpotDir", m_LightSpotDirections); |
|
|
|
cmd.SetGlobalBuffer("globalLightIndexList", m_LightIndexListBuffer); |
|
|
|
cmd.SetGlobalVector("globalLightData", new Vector4(pixelLightCount, m_ShadowLightIndex, m_Asset.ShadowMinNormalBias, m_Asset.ShadowNormalBias)); |
|
|
|
SetShaderKeywords(cmd, vertexLightCount > 0); |
|
|
|
if (!lightData.isSingleDirectionalLight) |
|
|
|
cmd.SetGlobalBuffer("globalLightIndexList", m_LightIndexListBuffer); |
|
|
|
cmd.SetGlobalVector("globalLightData", new Vector4(lightData.pixelLightsCount, m_ShadowLightIndex, m_Asset.ShadowMinNormalBias, m_Asset.ShadowNormalBias)); |
|
|
|
SetShaderKeywords(cmd, lightData.isSingleDirectionalLight, lightData.vertexLightsCount > 0); |
|
|
|
private bool RenderShadows(CullResults cullResults, VisibleLight shadowLight, ScriptableRenderContext context) |
|
|
|
private bool RenderShadows(ref CullResults cullResults, ref VisibleLight shadowLight, ref ScriptableRenderContext context) |
|
|
|
{ |
|
|
|
m_ShadowCasterCascadesCount = m_ShadowSettings.directionalLightCascadeCount; |
|
|
|
|
|
|
|
|
|
|
return resolution; |
|
|
|
} |
|
|
|
|
|
|
|
private void SetupShadowShaderVariables(ScriptableRenderContext context, int cascadeCount) |
|
|
|
private void SetupShadowShaderVariables(ref ScriptableRenderContext context, int cascadeCount) |
|
|
|
{ |
|
|
|
float shadowResolution = m_ShadowSlices[0].shadowResolution; |
|
|
|
|
|
|
|
|
|
|
setupShadow.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
private void SetShaderKeywords(CommandBuffer cmd, bool vertexLightSupport) |
|
|
|
private void SetShaderKeywords(CommandBuffer cmd, bool singleDirecitonal, bool vertexLightSupport) |
|
|
|
|
|
|
|
if (singleDirecitonal) |
|
|
|
cmd.EnableShaderKeyword("_SINGLE_DIRECTIONAL_LIGHT"); |
|
|
|
else |
|
|
|
cmd.DisableShaderKeyword("_SINGLE_DIRECTIONAL_LIGHT"); |
|
|
|
|
|
|
|
string[] shadowKeywords = new string[] { "_HARD_SHADOWS", "_SOFT_SHADOWS", "_HARD_SHADOWS_CASCADES", "_SOFT_SHADOWS_CASCADES" }; |
|
|
|
for (int i = 0; i < shadowKeywords.Length; ++i) |
|
|
|