using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace GraphicsCat.LitPlus.Experimental { // Fade in/out, supports composite objects public class LitPlusAlphaFadeController : MonoBehaviour { internal class PropertyID { readonly public static int ZWrite = Shader.PropertyToID("_ZWrite"); readonly public static int SrcBlend = Shader.PropertyToID("_SrcBlend"); readonly public static int DstBlend = Shader.PropertyToID("_DstBlend"); readonly public static int Color = Shader.PropertyToID("_BaseColor"); readonly public static int BlendMode = Shader.PropertyToID("_SURFACE_TYPE"); } internal class MaterialInfo { float m_OriginalZWrite; float m_OriginalSrcFac; float m_OriginalDstFac; Color m_OriginalColor; int m_OriginalRenderQueue; float m_OriginalBlendMode; public float originalAlpha { get { return m_OriginalColor.a; } } public void Backup(Material material) { m_OriginalBlendMode = material.GetFloat(PropertyID.BlendMode); m_OriginalZWrite = material.GetFloat(PropertyID.ZWrite); m_OriginalSrcFac = material.GetFloat(PropertyID.SrcBlend); m_OriginalDstFac = material.GetFloat(PropertyID.DstBlend); m_OriginalColor = material.GetColor(PropertyID.Color); m_OriginalRenderQueue = material.renderQueue; } public void Restore(Material material) { material.SetFloat(PropertyID.BlendMode, m_OriginalBlendMode); switch (m_OriginalBlendMode) { case 0: material.EnableKeyword("_SURFACE_TYPE_OPAQUE"); break; case 1: material.EnableKeyword("_SURFACE_TYPE_CUTOUT"); break; } material.renderQueue = m_OriginalRenderQueue; material.SetFloat(PropertyID.ZWrite, m_OriginalZWrite); material.SetFloat(PropertyID.SrcBlend, m_OriginalSrcFac); material.SetFloat(PropertyID.DstBlend, m_OriginalDstFac); material.SetColor(PropertyID.Color, m_OriginalColor); } public void OnBeginFade(Material material, float alpha) { // Still need to write depth, otherwise there may be confusion (e.g., hair and head) material.SetFloat(PropertyID.ZWrite, 1); material.SetFloat(PropertyID.SrcBlend, (int)BlendMode.SrcAlpha); material.SetFloat(PropertyID.DstBlend, (int)BlendMode.OneMinusSrcAlpha); var color = material.GetColor(PropertyID.Color); color.a = alpha; material.SetColor(PropertyID.Color, color); material.renderQueue = (int)RenderQueue.Transparent; material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); } } [Range(0.0f, 10.0f)] public float fadeTime = 1.0f; Dictionary m_MaterialInfos = new Dictionary(); bool m_IsFading = false; float m_FadeTimer = 0; bool m_FadeInOrOut = false; public void FadeIn() { Fade(true); } public void FadeOut() { Fade(false); } void Start() { CollectMaterials(); // Get all materials } void Update() { if (m_IsFading) // Alpha fade { m_FadeTimer += Time.deltaTime; var lerpFac = Mathf.Min(m_FadeTimer / fadeTime, 1); foreach (var kv in m_MaterialInfos) { var material = kv.Key; var materialInfo = kv.Value; var color = material.GetColor(PropertyID.Color); float alphaBegin = m_FadeInOrOut ? 0 : materialInfo.originalAlpha; float alphaEnd = m_FadeInOrOut ? materialInfo.originalAlpha : 0; color.a = Mathf.Lerp(alphaBegin, alphaEnd, lerpFac); material.SetColor(PropertyID.Color, color); } if (m_FadeTimer >= fadeTime) { m_IsFading = false; OnEndFade(); } } } void Fade(bool inOrOut) { m_IsFading = true; m_FadeTimer = 0; m_FadeInOrOut = inOrOut; OnBgeinFade(inOrOut ? 0 : 1); } void OnBgeinFade(float alpha) { foreach (var kv in m_MaterialInfos) { var material = kv.Key; var materialInfo = kv.Value; materialInfo.OnBeginFade(material, alpha); } } void OnEndFade() { foreach (var kv in m_MaterialInfos) { var material = kv.Key; var materialInfo = kv.Value; materialInfo.Restore(material); } } void CollectMaterials() { var renderres = gameObject.GetComponentsInChildren(false); foreach (var renderer in renderres) { // Cannot use ShareMaterials, otherwise it will affect other objects var materials = renderer.materials; if (materials == null) continue; foreach (var material in materials) { if (m_MaterialInfos.ContainsKey(material) == false) { if (material.shader.name.ToLower().IndexOf("graphicscat/litplus") != -1) { var info = new MaterialInfo(); info.Backup(material); // Collect original material data m_MaterialInfos.Add(material, info); } } } } } } }