using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Experimental.Rendering; namespace BadDog.Rendering.AreaLight { public class PreIntegratedFGD { [GenerateHLSL] public enum FGDTexture { Resolution = 64 } static PreIntegratedFGD s_Instance; public static PreIntegratedFGD instance { get { if (s_Instance == null) { s_Instance = new PreIntegratedFGD(); } return s_Instance; } } public enum FGDIndex { FGD_GGXAndDisneyDiffuse = 0, FGD_CharlieAndFabricLambert = 1, FGD_Marschner = 2, Count = 3 } bool[] m_isInit = new bool[(int)FGDIndex.Count]; int[] m_refCounting = new int[(int)FGDIndex.Count]; Material[] m_PreIntegratedFGDMaterial = new Material[(int)FGDIndex.Count]; RenderTexture[] m_PreIntegratedFGD = new RenderTexture[(int)FGDIndex.Count]; Shader[] m_CustomShaders = new Shader[(int)FGDIndex.Count]; PreIntegratedFGD() { for (int i = 0; i < (int)FGDIndex.Count; ++i) { m_isInit[i] = false; m_refCounting[i] = 0; m_CustomShaders[i] = null; } } /// /// Set custom shader for a specific FGD index. If null, will fallback to Shader.Find() /// public void SetShader(FGDIndex index, Shader shader) { if (index >= FGDIndex.Count) { Debug.LogError($"Invalid FGD index: {index}"); return; } m_CustomShaders[(int)index] = shader; } private Shader GetShaderForIndex(FGDIndex index) { // Use custom shader if set, otherwise fallback to Shader.Find() Shader customShader = m_CustomShaders[(int)index]; if (customShader != null) { return customShader; } switch (index) { case FGDIndex.FGD_GGXAndDisneyDiffuse: return Shader.Find("Hidden/BadDog/URP/PreIntegratedFGD_GGXDisneyDiffuse"); case FGDIndex.FGD_CharlieAndFabricLambert: return Shader.Find("Hidden/BadDog/URP/PreIntegratedFGD_CharlieFabricLambert"); case FGDIndex.FGD_Marschner: return Shader.Find("Hidden/BadDog/URP/PreIntegratedFGD_Marschner"); default: Debug.LogError($"Unable to get shader for index: {index}."); break; } return null; } public void Build(FGDIndex index) { Debug.Assert(index != FGDIndex.Count); Debug.Assert(m_refCounting[(int)index] >= 0); if (m_refCounting[(int)index] == 0) { Shader pixelShader = GetShaderForIndex(index); int res = (int)FGDTexture.Resolution; m_PreIntegratedFGDMaterial[(int)index] = CoreUtils.CreateEngineMaterial(pixelShader); m_PreIntegratedFGD[(int)index] = new RenderTexture(res, res, 0, GraphicsFormat.A2B10G10R10_UNormPack32) { hideFlags = HideFlags.HideAndDontSave, filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp, name = CoreUtils.GetRenderTargetAutoName(res, res, 1, GraphicsFormat.A2B10G10R10_UNormPack32, $"preIntegrated{index}") }; m_PreIntegratedFGD[(int)index].Create(); m_isInit[(int)index] = false; } m_refCounting[(int)index]++; } public void RenderInit(FGDIndex index, CommandBuffer cmd) { if (m_isInit[(int)index] && m_PreIntegratedFGD[(int)index].IsCreated()) { return; } // If we are in wireframe mode, the drawfullscreen will not work as expected, but we don't need the LUT anyway // So create the texture to avoid errors, it will be initialized by the first render without wireframe if (GL.wireframe) { m_PreIntegratedFGD[(int)index].Create(); return; } CoreUtils.DrawFullScreen(cmd, m_PreIntegratedFGDMaterial[(int)index], new RenderTargetIdentifier(m_PreIntegratedFGD[(int)index])); m_isInit[(int)index] = true; } public void Cleanup(FGDIndex index) { m_refCounting[(int)index]--; if (m_refCounting[(int)index] == 0) { CoreUtils.Destroy(m_PreIntegratedFGDMaterial[(int)index]); CoreUtils.Destroy(m_PreIntegratedFGD[(int)index]); m_isInit[(int)index] = false; } Debug.Assert(m_refCounting[(int)index] >= 0); } public void Bind(CommandBuffer cmd, FGDIndex index) { switch (index) { case FGDIndex.FGD_GGXAndDisneyDiffuse: cmd.SetGlobalTexture(PreIntegratedFGDShaderIDs._PreIntegratedFGD_GGXDisneyDiffuse, m_PreIntegratedFGD[(int)index]); break; case FGDIndex.FGD_CharlieAndFabricLambert: cmd.SetGlobalTexture(PreIntegratedFGDShaderIDs._PreIntegratedFGD_CharlieAndFabric, m_PreIntegratedFGD[(int)index]); break; case FGDIndex.FGD_Marschner: cmd.SetGlobalTexture(PreIntegratedFGDShaderIDs._PreIntegratedFGD_Marschner, m_PreIntegratedFGD[(int)index]); break; default: break; } } } }