using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace BadDog.Rendering.AreaLight { public partial class AreaLightingPass { partial void AllocateShadowmapIfNeeded() { if (m_CreateEmptyShadowmap) { // Allocate minimal 1x1 shadowmap for shader compatibility ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_AreaLightsShadowmapHandle, 1, 1, k_ShadowmapBufferBits, name: "_AreaLightsShadowmapTexture"); } else { // Allocate actual shadowmap atlas for rendering shadows ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_AreaLightsShadowmapHandle, m_RenderTargetWidth, m_RenderTargetHeight, k_ShadowmapBufferBits, name: "_AreaLightsShadowmapTexture"); } } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { if (m_AreaLightsShadowmapHandle != null) { ConfigureTarget(m_AreaLightsShadowmapHandle); if (m_EmptyShadowmapNeedsClear || !m_CreateEmptyShadowmap) { ConfigureClear(ClearFlag.All, Color.black); m_EmptyShadowmapNeedsClear = false; } } } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get(); using (new ProfilingScope(cmd, m_ProfilingSampler)) { // Enable global shader keyword for BG Area Lighting CoreUtils.SetKeyword(cmd, AreaLightingShaderIDs.ENABLE_BG_AREA_LIGHTING_KEYWORD, true); // Build LTC data texture (uses reference counting, safe to call multiple times) LTCAreaLight.instance.Build(); LTCAreaLight.instance.Bind(cmd); // GGX + Disney Diffuse PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse); PreIntegratedFGD.instance.RenderInit(PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse, cmd); PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse); // Charlie + Fabric Lambert PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert); PreIntegratedFGD.instance.RenderInit(PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert, cmd); PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert); // Marschner PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_Marschner); PreIntegratedFGD.instance.RenderInit(PreIntegratedFGD.FGDIndex.FGD_Marschner, cmd); PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_Marschner); var lightData = renderingData.lightData; var visibleLights = lightData.visibleLights; var areaLightDataArray = BGAreaLightManager.BuildAreaLightDataArray(visibleLights, lightData.mainLightIndex, m_LightSettings.maxAreaLights); var areaLightBuffer = BGAreaLightManager.UpdateGraphicsBuffer(areaLightDataArray); if (areaLightBuffer != null) { cmd.SetGlobalBuffer(AreaLightingShaderIDs._AreaLightDataBuffer, areaLightBuffer); cmd.SetGlobalInt(AreaLightingShaderIDs._AreaLightDataBufferCount, areaLightDataArray.IsCreated ? areaLightDataArray.Length : 0); } else { var emptyBuffer = BGAreaLightManager.GetEmptyBuffer(); cmd.SetGlobalBuffer(AreaLightingShaderIDs._AreaLightDataBuffer, emptyBuffer); cmd.SetGlobalInt(AreaLightingShaderIDs._AreaLightDataBufferCount, 0); } } if (m_CreateEmptyShadowmap) { SetShadowParamsForEmptyShadowmap(cmd); if (m_AreaLightsShadowmapHandle != null) { cmd.SetGlobalTexture(AreaLightingShaderIDs._AreaLightsShadowmapTexture, m_AreaLightsShadowmapHandle.nameID); } } else { RenderAreaLightShadows(context, ref renderingData, cmd); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } private void InitRendererLists(ref CullingResults cullResults, ref PassData passData, ScriptableRenderContext context) { if (m_CreateEmptyShadowmap) { return; } for (int shadowSliceIndex = 0; shadowSliceIndex < m_ValidShadowCastingLightsCount; ++shadowSliceIndex) { int areaLightIndex = m_ShadowSliceToAreaLightIndex[shadowSliceIndex]; int visibleLightIndex = m_AreaLightIndexToVisibleLightIndex[areaLightIndex]; if (visibleLightIndex < 0 || visibleLightIndex >= passData.visibleLights.Length) { passData.shadowRendererLists[shadowSliceIndex] = default; continue; } Light shadowLight = passData.visibleLights[visibleLightIndex].light; int layerMask = shadowLight != null ? shadowLight.cullingMask : ~0; bool useRenderingLayers = UniversalRenderPipeline.asset != null && UniversalRenderPipeline.asset.useRenderingLayers; uint renderingLayerMask = useRenderingLayers && shadowLight != null ? (uint)shadowLight.renderingLayerMask : uint.MaxValue; var sorting = new SortingSettings(passData.camera) { criteria = SortingCriteria.None }; var ds = new DrawingSettings(new ShaderTagId("ShadowCaster"), sorting); var fs = new FilteringSettings(RenderQueueRange.all, layerMask, renderingLayerMask); RendererListParams param = new RendererListParams(cullResults, ds, fs); passData.shadowRendererLists[shadowSliceIndex] = context.CreateRendererList(ref param); } } private void RenderAreaLightShadowmapAtlas(CommandBuffer cmd, ref PassData data) { using (new ProfilingScope(cmd, m_ShadowProfilingSampler)) { SetBGAreaLightShadowQualityKeywords(cmd); bool anyShadowSliceRendered = false; int shadowSlicesCount = m_ShadowSliceToAreaLightIndex.Count; Vector4 lastShadowBias = new Vector4(-10f, -10f, -10f, -10f); for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowSlicesCount; ++globalShadowSliceIndex) { int areaLightIndex = m_ShadowSliceToAreaLightIndex[globalShadowSliceIndex]; if (areaLightIndex < 0) { continue; } Vector4 shadowParams = m_AreaLightIndexToShadowParams[areaLightIndex]; // Check if shadow is valid: w != -1 and shadowStrength > 0 if (Mathf.Approximately(shadowParams.x, 0.0f) || Mathf.Approximately(shadowParams.w, -1.0f)) { continue; } int visibleLightIndex = m_AreaLightIndexToVisibleLightIndex[areaLightIndex]; if (visibleLightIndex < 0 || visibleLightIndex >= data.visibleLights.Length) { continue; } VisibleLight shadowLight = data.visibleLights[visibleLightIndex]; ShadowSliceData shadowSliceData = m_AreaLightsShadowSlices[globalShadowSliceIndex]; // Get nearPlane for this light (matching the logic in TryComputeAreaLightShadowSlice) Light unityLight = shadowLight.light; float nearPlane; if (unityLight != null && BGAreaLightManager.TryGetRegisteredAreaLight(unityLight, out var areaLightComp)) { nearPlane = areaLightComp.GetShadowNearPlane(); } else { nearPlane = unityLight != null ? unityLight.shadowNearPlane : 0.1f; } Vector4 shadowBias = AreaShadowUtils.GetRectangleLightShadowBias(ref shadowLight, ref m_ShadowDataCache, shadowSliceData.resolution, nearPlane, m_ShadowSettings.shadowFilter); if (globalShadowSliceIndex == 0 || !AreaShadowUtils.FastApproximately(shadowBias, lastShadowBias)) { SetShadowBias(cmd, shadowBias); lastShadowBias = shadowBias; } Vector3 lightPosition = shadowLight.localToWorldMatrix.GetColumn(3); SetLightPosition(cmd, lightPosition); RendererList shadowRendererList = data.shadowRendererLists[globalShadowSliceIndex]; RenderShadowSlice(cmd, ref shadowSliceData, ref shadowRendererList, shadowSliceData.projectionMatrix, shadowSliceData.viewMatrix); anyShadowSliceRendered = true; } if (anyShadowSliceRendered) { SetupAreaLightsShadowReceiverConstants(cmd, data.allocatedShadowAtlasSize); cmd.SetViewProjectionMatrices(data.viewMatrix, data.projectionMatrix); } } } private void InitPassData(ref PassData passData, in CameraData cameraData, in LightData lightData) { passData.pass = this; passData.emptyShadowmap = m_CreateEmptyShadowmap; passData.visibleLights = lightData.visibleLights; passData.viewMatrix = cameraData.GetViewMatrix(); passData.projectionMatrix = cameraData.GetProjectionMatrix(); passData.camera = cameraData.camera; passData.shadowRendererLists = m_ShadowRendererLists; } private void RenderAreaLightShadows(ScriptableRenderContext context, ref RenderingData renderingData, CommandBuffer cmd) { var cameraData = renderingData.cameraData; var lightData = renderingData.lightData; InitPassData(ref m_PassData, cameraData, lightData); InitRendererLists(ref renderingData.cullResults, ref m_PassData, context); m_PassData.allocatedShadowAtlasSize = new Vector2Int(m_RenderTargetWidth, m_RenderTargetHeight); RenderAreaLightShadowmapAtlas(cmd, ref m_PassData); if (m_AreaLightsShadowmapHandle != null) { cmd.SetGlobalTexture(AreaLightingShaderIDs._AreaLightsShadowmapTexture, m_AreaLightsShadowmapHandle.nameID); } } private void SetShadowBias(CommandBuffer cmd, Vector4 shadowBias) { cmd.SetGlobalVector(AreaLightingShaderIDs._ShadowBias, shadowBias); } private void SetLightPosition(CommandBuffer cmd, Vector3 lightPosition) { cmd.SetGlobalVector(AreaLightingShaderIDs._LightPosition, new Vector4(lightPosition.x, lightPosition.y, lightPosition.z, 1.0f)); } private void RenderShadowSlice(CommandBuffer cmd, ref ShadowSliceData shadowSliceData, ref RendererList shadowRendererList, Matrix4x4 proj, Matrix4x4 view) { cmd.SetGlobalDepthBias(1.0f, 2.5f); cmd.SetViewport(new Rect(shadowSliceData.offsetX, shadowSliceData.offsetY, shadowSliceData.resolution, shadowSliceData.resolution)); cmd.SetViewProjectionMatrices(view, proj); cmd.DrawRendererList(shadowRendererList); cmd.DisableScissorRect(); cmd.SetGlobalDepthBias(0.0f, 0.0f); } private void SetupAreaLightsShadowReceiverConstants(CommandBuffer cmd, Vector2Int atlasSize) { cmd.SetGlobalMatrixArray(AreaLightingShaderIDs._AreaLightsWorldToShadow, m_AreaLightShadowSliceIndexTo_WorldShadowMatrix); cmd.SetGlobalVectorArray(AreaLightingShaderIDs._AreaLightShadowParams, m_AreaLightIndexToShadowParams); cmd.SetGlobalVector(AreaLightingShaderIDs._AreaLightShadowmapSize, new Vector4(1f / atlasSize.x, 1f / atlasSize.y, atlasSize.x, atlasSize.y)); float pcssShadowSoftness = Mathf.Max(0f, m_ShadowSettings.pcssShadowSoftness); int pcssBlockerSampleCount = Mathf.Clamp(m_ShadowSettings.pcssBlockerSampleCount, 1, 64); int pcssFilterSampleCount = Mathf.Clamp(m_ShadowSettings.pcssFilterSampleCount, 1, 64); cmd.SetGlobalVector(AreaLightingShaderIDs._PCSSAdditionalLightParams, new Vector4(pcssShadowSoftness, pcssBlockerSampleCount, pcssFilterSampleCount, 1.0f)); } private static void SetShadowParamsForEmptyShadowmap(CommandBuffer cmd) { cmd.SetGlobalMatrixArray(AreaLightingShaderIDs._AreaLightsWorldToShadow, s_EmptyShadowMatrices); cmd.SetGlobalVectorArray(AreaLightingShaderIDs._AreaLightShadowParams, s_EmptyShadowParams); cmd.SetGlobalVector(AreaLightingShaderIDs._AreaLightShadowmapSize, new Vector4(0f, 0f, 1f, 1f)); cmd.SetGlobalVector(AreaLightingShaderIDs._PCSSAdditionalLightParams, Vector4.zero); } private void SetBGAreaLightShadowQualityKeywords(CommandBuffer cmd) { cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCF2x2, false); cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent5x5, false); cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent7x7, false); cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCSS, false); switch (m_ShadowSettings.shadowFilter) { case AreaLightShadowSettings.BGAreaLightShadowMode.PCF2x2: cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCF2x2, true); break; case AreaLightShadowSettings.BGAreaLightShadowMode.Tent5x5: cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent5x5, true); break; case AreaLightShadowSettings.BGAreaLightShadowMode.Tent7x7: cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent7x7, true); break; case AreaLightShadowSettings.BGAreaLightShadowMode.PCSS: cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCSS, true); break; case AreaLightShadowSettings.BGAreaLightShadowMode.Off: default: // All keywords disabled (handled above) break; } } } }