using System; using UnityEditor; using UnityEngine; public abstract class LightweightShaderGUI : ShaderGUI { public enum SurfaceType { Opaque, Transparent } public enum BlendMode { Alpha, // Old school alpha-blending mode, fresnel does not affect amount of transparency Premultiply, // Physically plausible transparency mode, implemented as alpha pre-multiply Additive, Multiply } private static class Styles { public static string renderingOptionsLabel = "Rendering Options"; public static string surfaceType = "Surface Type"; public static string blendingMode = "Blending Mode"; public static GUIContent twoSidedText = new GUIContent("Two Sided", "Render front and back faces"); public static GUIContent alphaClipText = new GUIContent("Alpha Clip", "Enable Alpha Clip"); public static GUIContent alphaClipThresholdText = new GUIContent("Clip Threshold", "Threshold for alpha clip"); public static GUIContent receiveShadowText = new GUIContent("Receive Shadows", "Enables this material to receive shadows if there is at least one shadow casting light affecting it."); public static readonly string[] surfaceNames = Enum.GetNames(typeof(SurfaceType)); public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode)); } protected MaterialEditor m_MaterialEditor; protected MaterialProperty surfaceTypeProp; protected MaterialProperty blendModeProp; protected MaterialProperty cullingProp; protected MaterialProperty alphaClipProp; protected MaterialProperty alphaCutoffProp; protected MaterialProperty receiveShadowsProp; private bool m_FirstTimeApply = true; public abstract void MaterialChanged(Material material); public virtual void FindProperties(MaterialProperty[] properties) { surfaceTypeProp = FindProperty("_Surface", properties); blendModeProp = FindProperty("_Blend", properties); cullingProp = FindProperty("_Cull", properties); alphaClipProp = FindProperty("_AlphaClip", properties); alphaCutoffProp = FindProperty("_Cutoff", properties); receiveShadowsProp = FindProperty("_ReceiveShadows", properties); } public virtual void ShaderPropertiesGUI(Material material) { DoPopup(Styles.surfaceType, surfaceTypeProp, Styles.surfaceNames); if ((SurfaceType)material.GetFloat("_Surface") == SurfaceType.Transparent) DoPopup(Styles.blendingMode, blendModeProp, Styles.blendNames); EditorGUI.BeginChangeCheck(); bool twoSidedEnabled = EditorGUILayout.Toggle(Styles.twoSidedText, cullingProp.floatValue == 0); if (EditorGUI.EndChangeCheck()) cullingProp.floatValue = twoSidedEnabled ? 0 : 2; EditorGUI.BeginChangeCheck(); bool alphaClipEnabled = EditorGUILayout.Toggle(Styles.alphaClipText, alphaClipProp.floatValue == 1); if (EditorGUI.EndChangeCheck()) alphaClipProp.floatValue = alphaClipEnabled ? 1 : 0; if (alphaClipProp.floatValue == 1) m_MaterialEditor.ShaderProperty(alphaCutoffProp, Styles.alphaClipThresholdText, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1); EditorGUI.BeginChangeCheck(); bool receiveShadows = EditorGUILayout.Toggle(Styles.receiveShadowText, receiveShadowsProp.floatValue == 1.0f); if (EditorGUI.EndChangeCheck()) receiveShadowsProp.floatValue = receiveShadows ? 1.0f : 0.0f; EditorGUILayout.Space(); } public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { FindProperties(properties); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly m_MaterialEditor = materialEditor; Material material = materialEditor.target as Material; // Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing // material to a lightweight shader. if (m_FirstTimeApply) { MaterialChanged(material); m_FirstTimeApply = false; } ShaderPropertiesGUI(material); } public static void SetupMaterialBlendMode(Material material) { bool alphaClip = material.GetFloat("_AlphaClip") == 1; if (alphaClip) material.EnableKeyword("_ALPHATEST_ON"); else material.DisableKeyword("_ALPHATEST_ON"); SurfaceType surfaceType = (SurfaceType)material.GetFloat("_Surface"); if (surfaceType == SurfaceType.Opaque) { material.SetOverrideTag("RenderType", ""); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); material.SetInt("_ZWrite", 1); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = -1; material.SetShaderPassEnabled("ShadowCaster", true); } else { BlendMode blendMode = (BlendMode)material.GetFloat("_Blend"); switch (blendMode) { case BlendMode.Alpha: material.SetOverrideTag("RenderType", "Transparent"); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); material.SetInt("_ZWrite", 0); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; material.SetShaderPassEnabled("ShadowCaster", false); break; case BlendMode.Premultiply: material.SetOverrideTag("RenderType", "Transparent"); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); material.SetInt("_ZWrite", 0); material.EnableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; material.SetShaderPassEnabled("ShadowCaster", false); break; case BlendMode.Additive: material.SetOverrideTag("RenderType", "Transparent"); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_ZWrite", 0); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; material.SetShaderPassEnabled("ShadowCaster", false); break; case BlendMode.Multiply: material.SetOverrideTag("RenderType", "Transparent"); material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.DstColor); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); material.SetInt("_ZWrite", 0); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; material.SetShaderPassEnabled("ShadowCaster", false); break; } } } protected void DoPopup(string label, MaterialProperty property, string[] options) { EditorGUI.showMixedValue = property.hasMixedValue; var mode = property.floatValue; EditorGUI.BeginChangeCheck(); mode = EditorGUILayout.Popup(label, (int)mode, options); if (EditorGUI.EndChangeCheck()) { m_MaterialEditor.RegisterPropertyChangeUndo(label); property.floatValue = (float)mode; } EditorGUI.showMixedValue = false; } protected void DoMaterialRenderingOptions() { EditorGUILayout.Space(); GUILayout.Label(Styles.renderingOptionsLabel, EditorStyles.boldLabel); m_MaterialEditor.EnableInstancingField(); m_MaterialEditor.DoubleSidedGIField(); } }