using System; using System.Reflection; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace BadDog.Rendering.AreaLight { public class ShadowAtlasResolutionAttribute : PropertyAttribute { } [Serializable] public class AreaLightSettings { [Tooltip("Maximum number of area lights processed per frame")] [Range(0, 8)] public int maxAreaLights = 8; [Tooltip("Pre-integrated FGD shader for GGX and Disney Diffuse")] public Shader preIntegratedFGD_GGXDisneyDiffuse; [Tooltip("Pre-integrated FGD shader for Charlie and Fabric Lambert")] public Shader preIntegratedFGD_CharlieFabricLambert; [Tooltip("Pre-integrated FGD shader for Marschner")] public Shader preIntegratedFGD_Marschner; } [Serializable] public class AreaLightShadowSettings { public enum BGAreaLightShadowMode { Off = 0, PCF2x2 = 1, Tent5x5 = 2, Tent7x7 = 3, PCSS = 4, } [Tooltip("Resolution of the area light shadow atlas (e.g., 512, 1024, 2048, 4096, 8192).)")] [ShadowAtlasResolution] public int shadowAtlasResolution = 1024; [Tooltip("Maximum number of area lights that can cast shadows simultaneously")] [Range(0, 8)] public int maxShadowCastingLights = 8; [Tooltip("Shadow filtering mode")] public BGAreaLightShadowMode shadowFilter = BGAreaLightShadowMode.Tent5x5; [Tooltip("Softness radius in world units for PCSS")] [Range(0.0f, 32.0f)] public float pcssShadowSoftness = 2f; [Tooltip("Number of samples used to search blockers in PCSS.")] [Range(4, 32)] public int pcssBlockerSampleCount = 16; [Tooltip("Number of samples used for PCSS filtering.")] [Range(4, 32)] public int pcssFilterSampleCount = 16; public string GetShadowKeyword() { switch (shadowFilter) { case BGAreaLightShadowMode.Off: return null; // No keyword = off case BGAreaLightShadowMode.PCF2x2: return "_BG_AREALIGHT_SHADOWS_PCF2X2"; case BGAreaLightShadowMode.Tent5x5: return "_BG_AREALIGHT_SHADOWS_TENT5X5"; case BGAreaLightShadowMode.Tent7x7: return "_BG_AREALIGHT_SHADOWS_TENT7X7"; case BGAreaLightShadowMode.PCSS: return "_BG_AREALIGHT_SHADOWS_PCSS"; default: return null; } } } public class AreaLighting : ScriptableRendererFeature { [SerializeField] private AreaLightSettings m_LightSettings = new AreaLightSettings(); [SerializeField] private AreaLightShadowSettings m_ShadowSettings = new AreaLightShadowSettings(); private AreaLightingPass m_AreaLightingPass; public override void Create() { if (m_AreaLightingPass == null) { m_AreaLightingPass = new AreaLightingPass(); } } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { GraphicsSettings.realtimeDirectRectangularAreaLights = true; m_AreaLightingPass.Setup(m_LightSettings, m_ShadowSettings, renderingData); renderer.EnqueuePass(m_AreaLightingPass); } protected override void Dispose(bool disposing) { m_AreaLightingPass?.Dispose(); m_AreaLightingPass = null; } } }