using System; namespace UnityEngine.Rendering.Universal.Internal { /// /// Renders a shadow map for the main Light. /// public class MainLightShadowCasterPass : ScriptableRenderPass { private static class MainLightShadowConstantBuffer { public static int _WorldToShadow; public static int _ShadowParams; public static int _CascadeShadowSplitSpheres0; public static int _CascadeShadowSplitSpheres1; public static int _CascadeShadowSplitSpheres2; public static int _CascadeShadowSplitSpheres3; public static int _CascadeShadowSplitSphereRadii; public static int _ShadowOffset0; public static int _ShadowOffset1; public static int _ShadowOffset2; public static int _ShadowOffset3; public static int _ShadowmapSize; } const int k_MaxCascades = 4; const int k_ShadowmapBufferBits = 16; int m_ShadowmapWidth; int m_ShadowmapHeight; int m_ShadowCasterCascadesCount; bool m_SupportsBoxFilterForShadows; RenderTargetHandle m_MainLightShadowmap; RenderTexture m_MainLightShadowmapTexture; Matrix4x4[] m_MainLightShadowMatrices; ShadowSliceData[] m_CascadeSlices; Vector4[] m_CascadeSplitDistances; const string m_ProfilerTag = "Render Main Shadowmap"; ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag); public MainLightShadowCasterPass(RenderPassEvent evt) { renderPassEvent = evt; m_MainLightShadowMatrices = new Matrix4x4[k_MaxCascades + 1]; m_CascadeSlices = new ShadowSliceData[k_MaxCascades]; m_CascadeSplitDistances = new Vector4[k_MaxCascades]; MainLightShadowConstantBuffer._WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow"); MainLightShadowConstantBuffer._ShadowParams = Shader.PropertyToID("_MainLightShadowParams"); MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0"); MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1"); MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2"); MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3 = Shader.PropertyToID("_CascadeShadowSplitSpheres3"); MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii = Shader.PropertyToID("_CascadeShadowSplitSphereRadii"); MainLightShadowConstantBuffer._ShadowOffset0 = Shader.PropertyToID("_MainLightShadowOffset0"); MainLightShadowConstantBuffer._ShadowOffset1 = Shader.PropertyToID("_MainLightShadowOffset1"); MainLightShadowConstantBuffer._ShadowOffset2 = Shader.PropertyToID("_MainLightShadowOffset2"); MainLightShadowConstantBuffer._ShadowOffset3 = Shader.PropertyToID("_MainLightShadowOffset3"); MainLightShadowConstantBuffer._ShadowmapSize = Shader.PropertyToID("_MainLightShadowmapSize"); m_MainLightShadowmap.Init("_MainLightShadowmapTexture"); m_SupportsBoxFilterForShadows = Application.isMobilePlatform || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Switch; } public bool Setup(ref RenderingData renderingData) { if (!renderingData.shadowData.supportsMainLightShadows) return false; Clear(); int shadowLightIndex = renderingData.lightData.mainLightIndex; if (shadowLightIndex == -1) return false; VisibleLight shadowLight = renderingData.lightData.visibleLights[shadowLightIndex]; Light light = shadowLight.light; if (light.shadows == LightShadows.None) return false; if (shadowLight.lightType != LightType.Directional) { Debug.LogWarning("Only directional lights are supported as main light."); } Bounds bounds; if (!renderingData.cullResults.GetShadowCasterBounds(shadowLightIndex, out bounds)) return false; m_ShadowCasterCascadesCount = renderingData.shadowData.mainLightShadowCascadesCount; int shadowResolution = ShadowUtils.GetMaxTileResolutionInAtlas(renderingData.shadowData.mainLightShadowmapWidth, renderingData.shadowData.mainLightShadowmapHeight, m_ShadowCasterCascadesCount); m_ShadowmapWidth = renderingData.shadowData.mainLightShadowmapWidth; m_ShadowmapHeight = (m_ShadowCasterCascadesCount == 2) ? renderingData.shadowData.mainLightShadowmapHeight >> 1 : renderingData.shadowData.mainLightShadowmapHeight; for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex) { bool success = ShadowUtils.ExtractDirectionalLightMatrix(ref renderingData.cullResults, ref renderingData.shadowData, shadowLightIndex, cascadeIndex, m_ShadowmapWidth, m_ShadowmapHeight, shadowResolution, light.shadowNearPlane, out m_CascadeSplitDistances[cascadeIndex], out m_CascadeSlices[cascadeIndex], out m_CascadeSlices[cascadeIndex].viewMatrix, out m_CascadeSlices[cascadeIndex].projectionMatrix); if (!success) return false; } return true; } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { m_MainLightShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(m_ShadowmapWidth, m_ShadowmapHeight, k_ShadowmapBufferBits); ConfigureTarget(new RenderTargetIdentifier(m_MainLightShadowmapTexture)); ConfigureClear(ClearFlag.All, Color.black); } /// public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { RenderMainLightCascadeShadowmap(ref context, ref renderingData.cullResults, ref renderingData.lightData, ref renderingData.shadowData); } /// public override void FrameCleanup(CommandBuffer cmd) { if (cmd == null) throw new ArgumentNullException("cmd"); if (m_MainLightShadowmapTexture) { RenderTexture.ReleaseTemporary(m_MainLightShadowmapTexture); m_MainLightShadowmapTexture = null; } } void Clear() { m_MainLightShadowmapTexture = null; for (int i = 0; i < m_MainLightShadowMatrices.Length; ++i) m_MainLightShadowMatrices[i] = Matrix4x4.identity; for (int i = 0; i < m_CascadeSplitDistances.Length; ++i) m_CascadeSplitDistances[i] = new Vector4(0.0f, 0.0f, 0.0f, 0.0f); for (int i = 0; i < m_CascadeSlices.Length; ++i) m_CascadeSlices[i].Clear(); } void RenderMainLightCascadeShadowmap(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData) { int shadowLightIndex = lightData.mainLightIndex; if (shadowLightIndex == -1) return; VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex]; CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag); using (new ProfilingScope(cmd, m_ProfilingSampler)) { var settings = new ShadowDrawingSettings(cullResults, shadowLightIndex); for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex) { var splitData = settings.splitData; splitData.cullingSphere = m_CascadeSplitDistances[cascadeIndex]; settings.splitData = splitData; Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution); ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias); ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_CascadeSlices[cascadeIndex], ref settings, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix); } bool softShadows = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows; CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, true); CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1); CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, softShadows); SetupMainLightShadowReceiverConstants(cmd, shadowLight, softShadows); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } void SetupMainLightShadowReceiverConstants(CommandBuffer cmd, VisibleLight shadowLight, bool softShadows) { Light light = shadowLight.light; int cascadeCount = m_ShadowCasterCascadesCount; for (int i = 0; i < cascadeCount; ++i) m_MainLightShadowMatrices[i] = m_CascadeSlices[i].shadowTransform; // We setup and additional a no-op WorldToShadow matrix in the last index // because the ComputeCascadeIndex function in Shadows.hlsl can return an index // out of bounds. (position not inside any cascade) and we want to avoid branching Matrix4x4 noOpShadowMatrix = Matrix4x4.zero; noOpShadowMatrix.m22 = (SystemInfo.usesReversedZBuffer) ? 1.0f : 0.0f; for (int i = cascadeCount; i <= k_MaxCascades; ++i) m_MainLightShadowMatrices[i] = noOpShadowMatrix; float invShadowAtlasWidth = 1.0f / m_ShadowmapWidth; float invShadowAtlasHeight = 1.0f / m_ShadowmapHeight; float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth; float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight; float softShadowsProp = softShadows ? 1.0f : 0.0f; cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture); cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams, new Vector4(light.shadowStrength, softShadowsProp, 0.0f, 0.0f)); if (m_ShadowCasterCascadesCount > 1) { cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0, m_CascadeSplitDistances[0]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1, m_CascadeSplitDistances[1]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2, m_CascadeSplitDistances[2]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3, m_CascadeSplitDistances[3]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii, new Vector4( m_CascadeSplitDistances[0].w * m_CascadeSplitDistances[0].w, m_CascadeSplitDistances[1].w * m_CascadeSplitDistances[1].w, m_CascadeSplitDistances[2].w * m_CascadeSplitDistances[2].w, m_CascadeSplitDistances[3].w * m_CascadeSplitDistances[3].w)); } if (softShadows) { if (m_SupportsBoxFilterForShadows) { cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset0, new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset1, new Vector4(invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset2, new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset3, new Vector4(invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f)); } // Currently only used when !SHADER_API_MOBILE but risky to not set them as it's generic // enough so custom shaders might use it. cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, new Vector4(invShadowAtlasWidth, invShadowAtlasHeight, m_ShadowmapWidth, m_ShadowmapHeight)); } } }; }