|
|
|
|
|
|
|
|
|
|
private static readonly int kMaxVertexLights = 4; |
|
|
|
|
|
|
|
// We have no good approach exposed to skip shader variants, e.g, ideally we would like to skip _CASCADE for all punctual lights
|
|
|
|
// We combine light and shadow classification keywords to reduce the amount of shader variants.
|
|
|
|
// Lightweight shader library declares defines based on these keywords to avoid having to check them in the shaders
|
|
|
|
// Core.hlsl defines _MAIN_LIGHT_DIRECTIONAL and _MAIN_LIGHT_SPOT (point lights can't be main light)
|
|
|
|
// Shadow.hlsl defines _SHADOWS_ENABLED, _SHADOWS_SOFT, _SHADOWS_CASCADE, _SHADOWS_PERSPECTIVE
|
|
|
|
private static readonly string[] kMainLightKeywords = |
|
|
|
{ |
|
|
|
"_MAIN_LIGHT_DIRECTIONAL_SHADOW", |
|
|
|
"_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE", |
|
|
|
"_MAIN_LIGHT_DIRECTIONAL_SHADOW_SOFT", |
|
|
|
"_MAIN_LIGHT_DIRECTIONAL_SHADOW_CASCADE_SOFT", |
|
|
|
|
|
|
|
"_MAIN_LIGHT_SPOT_SHADOW", |
|
|
|
"_MAIN_LIGHT_SPOT_SHADOW_SOFT" |
|
|
|
}; |
|
|
|
|
|
|
|
private StringBuilder m_MainLightKeywordString = new StringBuilder(43); |
|
|
|
|
|
|
|
private bool m_IsOffscreenCamera; |
|
|
|
|
|
|
|
private Vector4 kDefaultLightPosition = new Vector4(0.0f, 0.0f, 1.0f, 0.0f); |
|
|
|
|
|
|
private bool m_RequireDepthTexture; |
|
|
|
private bool m_RequireCopyColor; |
|
|
|
private bool m_DepthRenderBuffer; |
|
|
|
private bool m_RequireScreenSpaceShadows; |
|
|
|
private MixedLightingSetup m_MixedLightingSetup; |
|
|
|
|
|
|
|
private const int kDepthStencilBufferBits = 32; |
|
|
|
|
|
|
LightData lightData; |
|
|
|
InitializeLightData(visibleLights, out lightData); |
|
|
|
|
|
|
|
ShadowPass(visibleLights, ref context, ref lightData); |
|
|
|
bool shadows = ShadowPass(visibleLights, ref context, ref lightData); |
|
|
|
SetupFrameRenderingConfiguration(out frameRenderingConfiguration, stereoEnabled); |
|
|
|
SetupFrameRenderingConfiguration(out frameRenderingConfiguration, shadows, stereoEnabled); |
|
|
|
SetupIntermediateResources(frameRenderingConfiguration, ref context); |
|
|
|
|
|
|
|
// SetupCameraProperties does the following:
|
|
|
|
|
|
|
if (LightweightUtils.HasFlag(frameRenderingConfiguration, FrameRenderingConfiguration.DepthPrePass)) |
|
|
|
{ |
|
|
|
DepthPass(ref context); |
|
|
|
if(m_RequireScreenSpaceShadows) |
|
|
|
|
|
|
|
// Only screen space shadowmap mode is supported.
|
|
|
|
if (shadows) |
|
|
|
// Release temporary RT
|
|
|
|
cmd.ReleaseTemporaryRT(m_ScreenSpaceShadowMapRTID); |
|
|
|
cmd.ReleaseTemporaryRT(CameraRenderTargetID.depthCopy); |
|
|
|
cmd.ReleaseTemporaryRT(CameraRenderTargetID.depth); |
|
|
|
cmd.ReleaseTemporaryRT(CameraRenderTargetID.color); |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void ShadowPass(List<VisibleLight> visibleLights, ref ScriptableRenderContext context, ref LightData lightData) |
|
|
|
private bool ShadowPass(List<VisibleLight> visibleLights, ref ScriptableRenderContext context, ref LightData lightData) |
|
|
|
{ |
|
|
|
if (m_Asset.AreShadowsEnabled() && lightData.mainLightIndex != -1) |
|
|
|
{ |
|
|
|
|
|
|
if (!LightweightUtils.IsSupportedShadowType(mainLight.lightType)) |
|
|
|
{ |
|
|
|
Debug.LogWarning("Only directional and spot shadows are supported by LightweightPipeline."); |
|
|
|
return; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// There's no way to map shadow light indices. We need to pass in the original unsorted index.
|
|
|
|
|
|
|
{ |
|
|
|
lightData.shadowMapSampleType = LightShadows.None; |
|
|
|
} |
|
|
|
|
|
|
|
return shadowsRendered; |
|
|
|
|
|
|
|
return false; |
|
|
|
if (m_Asset.AreShadowsEnabled() && lightData.mainLightIndex != -1) |
|
|
|
{ |
|
|
|
CommandBuffer cmd = CommandBufferPool.Get("Collect Shadows"); |
|
|
|
CommandBuffer cmd = CommandBufferPool.Get("Collect Shadows"); |
|
|
|
//Keywords and constants are set up in advance for the collect pass.
|
|
|
|
SetupShadowReceiverConstants(cmd, visibleLights[lightData.mainLightIndex]); |
|
|
|
SetMainLightShaderKeywords(cmd, ref lightData, visibleLights); |
|
|
|
SetupShadowReceiverConstants(cmd, visibleLights[lightData.mainLightIndex]); |
|
|
|
SetShadowCollectPassKeywords(cmd, ref lightData); |
|
|
|
cmd.GetTemporaryRT(m_ScreenSpaceShadowMapRTID, m_CurrCamera.pixelWidth, m_CurrCamera.pixelHeight, 0, FilterMode.Bilinear, RenderTextureFormat.R8); |
|
|
|
cmd.Blit(null, m_ScreenSpaceShadowMapRT, m_ScreenSpaceShadowsMaterial); |
|
|
|
cmd.GetTemporaryRT(m_ScreenSpaceShadowMapRTID, m_CurrCamera.pixelWidth, m_CurrCamera.pixelHeight, 0, FilterMode.Bilinear, RenderTextureFormat.R8); |
|
|
|
cmd.Blit(null, m_ScreenSpaceShadowMapRT, m_ScreenSpaceShadowsMaterial); |
|
|
|
context.ExecuteCommandBuffer(cmd); |
|
|
|
CommandBufferPool.Release(cmd); |
|
|
|
} |
|
|
|
context.ExecuteCommandBuffer(cmd); |
|
|
|
CommandBufferPool.Release(cmd); |
|
|
|
} |
|
|
|
|
|
|
|
private void DepthPass(ref ScriptableRenderContext context) |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void SetupFrameRenderingConfiguration(out FrameRenderingConfiguration configuration, bool stereoEnabled) |
|
|
|
private void SetupFrameRenderingConfiguration(out FrameRenderingConfiguration configuration, bool shadows, bool stereoEnabled) |
|
|
|
{ |
|
|
|
configuration = (stereoEnabled) ? FrameRenderingConfiguration.Stereo : FrameRenderingConfiguration.None; |
|
|
|
if (stereoEnabled && XRSettings.eyeTextureDesc.dimension == TextureDimension.Tex2DArray) |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
m_RequireScreenSpaceShadows = m_Asset.CascadeCount > 1; |
|
|
|
if (m_RequireScreenSpaceShadows) |
|
|
|
if (shadows) |
|
|
|
if(!msaaEnabled) |
|
|
|
if (!msaaEnabled) |
|
|
|
intermediateTexture = true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
// If msaa is enabled we don't use a depth renderbuffer as we might not have support to Texture2DMS to resolve depth.
|
|
|
|
// Instead we use a depth prepass and whenever depth is needed we use the 1 sample depth from prepass.
|
|
|
|
// Screen space shadows require depth before opaque shading.
|
|
|
|
if (!msaaEnabled && !m_RequireScreenSpaceShadows) |
|
|
|
if (!msaaEnabled && !shadows) |
|
|
|
{ |
|
|
|
bool supportsDepthCopy = m_CopyTextureSupport != CopyTextureSupport.None && m_Asset.CopyDepthShader.isSupported; |
|
|
|
m_DepthRenderBuffer = true; |
|
|
|
|
|
|
configuration |= FrameRenderingConfiguration.DepthPrePass; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Rect cameraRect = m_CurrCamera.rect; |
|
|
|
if (!(Math.Abs(cameraRect.x) > 0.0f || Math.Abs(cameraRect.y) > 0.0f || Math.Abs(cameraRect.width) < 1.0f || Math.Abs(cameraRect.height) < 1.0f)) |
|
|
|
configuration |= FrameRenderingConfiguration.DefaultViewport; |
|
|
|
|
|
|
// Main light has an optimized shader path for main light. This will benefit games that only care about a single light.
|
|
|
|
// Lightweight pipeline also supports only a single shadow light, if available it will be the main light.
|
|
|
|
SetupMainLightConstants(cmd, lights, lightData.mainLightIndex); |
|
|
|
if (lightData.shadowMapSampleType != LightShadows.None && !m_RequireScreenSpaceShadows) |
|
|
|
SetupShadowReceiverConstants(cmd, lights[lightData.mainLightIndex]); |
|
|
|
|
|
|
|
SetupAdditionalListConstants(cmd, lights, ref lightData); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
cmd.SetGlobalVectorArray(ShadowConstantBuffer._DirShadowSplitSpheres, m_DirectionalShadowSplitDistances); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._DirShadowSplitSphereRadii, m_DirectionalShadowSplitRadii); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset0, new Vector4(-invHalfShadowResolution, -invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset1, new Vector4( invHalfShadowResolution, -invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset2, new Vector4(-invHalfShadowResolution, invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset3, new Vector4( invHalfShadowResolution, invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset1, new Vector4(invHalfShadowResolution, -invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset2, new Vector4(-invHalfShadowResolution, invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVector(ShadowConstantBuffer._ShadowOffset3, new Vector4(invHalfShadowResolution, invHalfShadowResolution, 0.0f, 0.0f)); |
|
|
|
cmd.SetGlobalVectorArray(ShadowConstantBuffer._FrustumCorners, m_RequireScreenSpaceShadows ? LightweightUtils.GetFarPlaneCorners(m_CurrCamera) : new Vector4[4] ); |
|
|
|
cmd.SetGlobalVectorArray(ShadowConstantBuffer._FrustumCorners, LightweightUtils.GetFarPlaneCorners(m_CurrCamera)); |
|
|
|
} |
|
|
|
|
|
|
|
private void SetShaderKeywords(CommandBuffer cmd, ref LightData lightData, List<VisibleLight> visibleLights) |
|
|
|
|
|
|
if(!m_RequireScreenSpaceShadows) |
|
|
|
SetMainLightShaderKeywords(cmd, ref lightData, visibleLights); |
|
|
|
|
|
|
|
int mainLightIndex = lightData.mainLightIndex; |
|
|
|
CoreUtils.SetKeyword(cmd, "_MAIN_LIGHT_DIRECTIONAL", mainLightIndex == -1 || visibleLights[mainLightIndex].lightType == LightType.Directional); |
|
|
|
CoreUtils.SetKeyword(cmd, "_MAIN_LIGHT_SPOT", mainLightIndex != -1 && visibleLights[mainLightIndex].lightType == LightType.Spot); |
|
|
|
CoreUtils.SetKeyword(cmd, "_SHADOWS_ENABLED", lightData.shadowMapSampleType != LightShadows.None); |
|
|
|
CoreUtils.SetKeyword(cmd, "_MAIN_LIGHT_COOKIE", mainLightIndex != -1 && LightweightUtils.IsSupportedCookieType(visibleLights[mainLightIndex].lightType) && visibleLights[mainLightIndex].light.cookie != null); |
|
|
|
CoreUtils.SetKeyword(cmd, "_ADDITIONAL_LIGHTS", lightData.totalAdditionalLightsCount > 0); |
|
|
|
CoreUtils.SetKeyword(cmd, "_MIXED_LIGHTING_SUBTRACTIVE", m_MixedLightingSetup == MixedLightingSetup.Subtractive); |
|
|
|
CoreUtils.SetKeyword(cmd, "_VERTEX_LIGHTS", vertexLightsCount > 0); |
|
|
|
|
|
|
CoreUtils.SetKeyword(cmd, "FOG_EXP2", exponentialFogModeEnabled); |
|
|
|
} |
|
|
|
|
|
|
|
private void SetMainLightShaderKeywords(CommandBuffer cmd, ref LightData lightData, List<VisibleLight> visibleLights) |
|
|
|
private void SetShadowCollectPassKeywords(CommandBuffer cmd, ref LightData lightData) |
|
|
|
int mainLightIndex = lightData.mainLightIndex; |
|
|
|
|
|
|
|
for (int i = 0; i < kMainLightKeywords.Length; ++i) |
|
|
|
cmd.DisableShaderKeyword(kMainLightKeywords[i]); |
|
|
|
|
|
|
|
if (mainLightIndex != -1 && (lightData.shadowMapSampleType != LightShadows.None)) |
|
|
|
{ |
|
|
|
m_MainLightKeywordString.Length = 0; |
|
|
|
m_MainLightKeywordString.Append("_MAIN_LIGHT"); |
|
|
|
LightType mainLightType = visibleLights[mainLightIndex].lightType; |
|
|
|
if (mainLightType == LightType.Directional) |
|
|
|
{ |
|
|
|
m_MainLightKeywordString.Append("_DIRECTIONAL_SHADOW"); |
|
|
|
if (m_Asset.CascadeCount > 1) |
|
|
|
m_MainLightKeywordString.Append("_CASCADE"); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
m_MainLightKeywordString.Append("_SPOT_SHADOW"); |
|
|
|
} |
|
|
|
|
|
|
|
if (lightData.shadowMapSampleType == LightShadows.Soft) |
|
|
|
m_MainLightKeywordString.Append("_SOFT"); |
|
|
|
string keyword = m_MainLightKeywordString.ToString(); |
|
|
|
cmd.EnableShaderKeyword(keyword); |
|
|
|
} |
|
|
|
|
|
|
|
CoreUtils.SetKeyword(cmd, "_MAIN_LIGHT_COOKIE", mainLightIndex != -1 && LightweightUtils.IsSupportedCookieType(visibleLights[mainLightIndex].lightType) && visibleLights[mainLightIndex].light.cookie != null); |
|
|
|
CoreUtils.SetKeyword(cmd, "_SHADOWS_SOFT", lightData.shadowMapSampleType == LightShadows.Soft); |
|
|
|
CoreUtils.SetKeyword(cmd, "_SHADOWS_CASCADE", lightData.shadowMapSampleType != LightShadows.None && m_Asset.CascadeCount > 1); |
|
|
|
} |
|
|
|
|
|
|
|
private bool RenderShadows(ref CullResults cullResults, ref VisibleLight shadowLight, int shadowLightIndex, ref ScriptableRenderContext context) |
|
|
|