using System; using UnityEngine; using UnityEngine.Rendering; using RenderQueue = UnityEngine.Rendering.RenderQueue; using UnityEditor.ShaderGraph.Drawing; namespace UnityEditor.Rendering.Fullscreen.ShaderGraph { /// /// The base class to implement the fullscreen Material GUI in a render pipeline. /// public class FullscreenShaderGUI : ShaderGUI { /// Enum used to store the expanded state of the drawer in the material GUI [Flags] protected enum Expandable { /// Surface Option key for the MaterialHeaderScopeList.RegisterHeaderScope call SurfaceOptions = 1 << 0, /// Surface Inputs key for the MaterialHeaderScopeList.RegisterHeaderScope call SurfaceInputs = 1 << 1, } protected class Styles { // Categories /// Surface Option header name public static readonly GUIContent SurfaceOptions = EditorGUIUtility.TrTextContent("Surface Options", "Controls the rendering states of the fullscreen material."); /// Surface Inputs header name public static readonly GUIContent SurfaceInputs = EditorGUIUtility.TrTextContent("Surface Inputs", "These settings describe the look and feel of the surface itself."); /// Name and tooltip for the blending mode property in the material GUI public static readonly GUIContent blendingMode = EditorGUIUtility.TrTextContent("Blending Mode", "Controls how the color of the Transparent surface blends with the Material color in the background."); /// Name and tooltip for the source color blend mode property in the material GUI public static readonly GUIContent srcColorBlendMode = EditorGUIUtility.TrTextContent("Src Color", "Describes how the input color will be blended."); /// Name and tooltip for the destination color blend mode property in the material GUI public static readonly GUIContent dstColorBlendMode = EditorGUIUtility.TrTextContent("Dst Color", "Describes how the destination color will be blended."); /// Name and tooltip for the color blend operation property in the material GUI public static readonly GUIContent colorBlendOperation = EditorGUIUtility.TrTextContent("Color Blend Op", "Tell which operation to use when blending the colors. Default is Add."); /// Name and tooltip for the source alpha blend mode property in the material GUI public static readonly GUIContent srcAlphaBlendMode = EditorGUIUtility.TrTextContent("Src Alpha", "Describes how the input alpha will be blended."); /// Name and tooltip for the destination alpha blend mode property in the material GUI public static readonly GUIContent dstAlphaBlendMode = EditorGUIUtility.TrTextContent("Dst Alpha", "Describes how the input alpha will be blended."); /// Name and tooltip for the alpha blend operation property in the material GUI public static readonly GUIContent alphaBlendOperation = EditorGUIUtility.TrTextContent("Alpha Blend Op", "Tell which operation to use when blending the alpha channel. Default is Add."); /// Name and tooltip for the depth write property in the material GUI public static readonly GUIContent depthWrite = EditorGUIUtility.TrTextContent("Depth Write", "Controls whether the shader writes depth."); /// Name and tooltip for the depth test property in the material GUI public static readonly GUIContent depthTest = EditorGUIUtility.TrTextContent("Depth Test", "Specifies the depth test mode. The default is Always."); /// Name and tooltip for the stencil override property in the material GUI public static readonly GUIContent stencil = EditorGUIUtility.TrTextContent("Stencil Override", "Enable the stencil block in the shader."); /// Name and tooltip for the stencil reference property in the material GUI public static readonly GUIContent stencilRef = EditorGUIUtility.TrTextContent("Reference", "Reference value use for comparison and operations."); /// Name and tooltip for the stencil read mask property in the material GUI public static readonly GUIContent stencilReadMask = EditorGUIUtility.TrTextContent("Read Mask", "Tells which bit are allowed to be read during the stencil test."); /// Name and tooltip for the stencil write mask property in the material GUI public static readonly GUIContent stencilWriteMask = EditorGUIUtility.TrTextContent("Write Mask", "Tells which bit are allowed to be written during the stencil test."); /// Name and tooltip for the stencil comparison property in the material GUI public static readonly GUIContent stencilComparison = EditorGUIUtility.TrTextContent("Comparison", "Tells which function to use when doing the stencil test."); /// Name and tooltip for the stencil pass operation property in the material GUI public static readonly GUIContent stencilPass = EditorGUIUtility.TrTextContent("Pass", "Tells what to do when the stencil test succeed."); /// Name and tooltip for the stencil fail operation property in the material GUI public static readonly GUIContent stencilFail = EditorGUIUtility.TrTextContent("Fail", "Tells what to do when the stencil test fails."); /// Name and tooltip for the stencil depth fail operation property in the material GUI public static readonly GUIContent stencilDepthFail = EditorGUIUtility.TrTextContent("Depth Fail", "Tells what to do when the depth test fails."); } bool m_FirstTimeApply = true; // By default, everything is expanded readonly MaterialHeaderScopeList m_MaterialScopeList = new MaterialHeaderScopeList(uint.MaxValue); // These have to be stored due to how MaterialHeaderScopeList callbacks work (they don't provide this data in the callbacks) MaterialEditor m_MaterialEditor; MaterialProperty[] m_Properties; private const int queueOffsetRange = 50; /// /// Unity calls this function when it displays the GUI. To implement your custom GUI, override this function.. /// /// Material editor instance. /// The list of properties in the inspected material(s). override public void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { m_MaterialEditor = materialEditor; m_Properties = properties; Material targetMat = materialEditor.target as Material; if (m_FirstTimeApply) { OnOpenGUI(targetMat, materialEditor, properties); m_FirstTimeApply = false; } ShaderPropertiesGUI(materialEditor, targetMat, properties); } /// /// Unity calls this function when it displays the GUI. To implement your custom GUI, override this function.. /// /// Material editor instance. /// The list of properties in the inspected material(s). /// The target materials for this GUI. public virtual void OnOpenGUI(Material material, MaterialEditor materialEditor, MaterialProperty[] properties) { // Generate the foldouts m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceOptions, (uint)Expandable.SurfaceOptions, DrawSurfaceOptions); m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceInputs, (uint)Expandable.SurfaceInputs, DrawSurfaceInputs); } /// /// Assign a new FullscreenShader to the target material. /// /// A valid material using a Fullscreen Shader Graph. /// /// public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) { // Clear all keywords for fresh start // Note: this will nuke user-selected custom keywords when they change shaders material.shaderKeywords = null; base.AssignNewShaderToMaterial(material, oldShader, newShader); // Setup keywords based on the new shader ValidateMaterial(material); } void ShaderPropertiesGUI(MaterialEditor materialEditor, Material material, MaterialProperty[] properties) { m_MaterialScopeList.DrawHeaders(materialEditor, material); } /// /// Draw the Surface Options section of the fullscreen shader GUI. /// /// A valid material using a Fullscreen Shader Graph. protected virtual void DrawSurfaceOptions(Material material) { var materialEditor = m_MaterialEditor; var properties = m_Properties; var blendMode = FindProperty(FullscreenUniforms.blendModeProperty, properties, false); var srcColorBlend = FindProperty(FullscreenUniforms.srcColorBlendProperty, properties, false); var dstColorBlend = FindProperty(FullscreenUniforms.dstColorBlendProperty, properties, false); var srcAlphaBlend = FindProperty(FullscreenUniforms.srcAlphaBlendProperty, properties, false); var dstAlphaBlend = FindProperty(FullscreenUniforms.dstAlphaBlendProperty, properties, false); var colorBlendOp = FindProperty(FullscreenUniforms.colorBlendOperationProperty, properties, false); var alphaBlendOp = FindProperty(FullscreenUniforms.alphaBlendOperationProperty, properties, false); var depthWrite = FindProperty(FullscreenUniforms.depthWriteProperty, properties, false); var depthTest = FindProperty(FullscreenUniforms.depthTestProperty, properties, false); var stencilEnable = FindProperty(FullscreenUniforms.stencilEnableProperty, properties, false); var stencilRef = FindProperty(FullscreenUniforms.stencilReferenceProperty, properties, false); var stencilReadMask = FindProperty(FullscreenUniforms.stencilReadMaskProperty, properties, false); var stencilWriteMask = FindProperty(FullscreenUniforms.stencilWriteMaskProperty, properties, false); var stencilComp = FindProperty(FullscreenUniforms.stencilComparisonProperty, properties, false); var stencilPass = FindProperty(FullscreenUniforms.stencilPassProperty, properties, false); var stencilFail = FindProperty(FullscreenUniforms.stencilFailProperty, properties, false); var stencilDepthFail = FindProperty(FullscreenUniforms.stencilDepthFailProperty, properties, false); if (material.HasProperty(FullscreenUniforms.blendModeProperty)) { EditorGUI.BeginChangeCheck(); m_MaterialEditor.ShaderProperty(blendMode, Styles.blendingMode); FullscreenBlendMode blendModeValue = (FullscreenBlendMode)blendMode.floatValue; if (EditorGUI.EndChangeCheck()) SetBlendMode(blendModeValue); if (blendModeValue == FullscreenBlendMode.Custom) { m_MaterialEditor.ShaderProperty(srcColorBlend, Styles.srcColorBlendMode, 1); m_MaterialEditor.ShaderProperty(dstColorBlend, Styles.dstColorBlendMode, 1); m_MaterialEditor.ShaderProperty(colorBlendOp, Styles.colorBlendOperation, 1); m_MaterialEditor.ShaderProperty(srcAlphaBlend, Styles.srcAlphaBlendMode, 1); m_MaterialEditor.ShaderProperty(dstAlphaBlend, Styles.dstAlphaBlendMode, 1); m_MaterialEditor.ShaderProperty(alphaBlendOp, Styles.alphaBlendOperation, 1); } } if (material.HasProperty(FullscreenUniforms.depthWriteProperty)) m_MaterialEditor.ShaderProperty(depthWrite, Styles.depthWrite); if (material.HasProperty(FullscreenUniforms.depthTestProperty)) m_MaterialEditor.ShaderProperty(depthTest, Styles.depthTest); if (material.HasProperty(FullscreenUniforms.stencilEnableProperty)) { EditorGUI.BeginChangeCheck(); m_MaterialEditor.ShaderProperty(stencilEnable, Styles.stencil); bool stencilEnableValue = stencilEnable.floatValue > 0.5f; if (EditorGUI.EndChangeCheck()) SetStencilEnable(stencilEnableValue); if (stencilEnableValue) { m_MaterialEditor.ShaderProperty(stencilRef, Styles.stencilRef, 1); m_MaterialEditor.ShaderProperty(stencilReadMask, Styles.stencilReadMask, 1); m_MaterialEditor.ShaderProperty(stencilWriteMask, Styles.stencilWriteMask, 1); m_MaterialEditor.ShaderProperty(stencilComp, Styles.stencilComparison, 1); m_MaterialEditor.ShaderProperty(stencilPass, Styles.stencilPass, 1); m_MaterialEditor.ShaderProperty(stencilFail, Styles.stencilFail, 1); m_MaterialEditor.ShaderProperty(stencilDepthFail, Styles.stencilDepthFail, 1); } } void SetStencilEnable(bool enabled) { if (!enabled) { stencilComp.floatValue = (float)CompareFunction.Always; stencilPass.floatValue = (float)StencilOp.Keep; stencilFail.floatValue = (float)StencilOp.Keep; stencilDepthFail.floatValue = (float)StencilOp.Keep; } } void SetBlendMode(FullscreenBlendMode blendMode) { // Note that we can't disable the blend mode from here if (blendMode == FullscreenBlendMode.Alpha || blendMode == FullscreenBlendMode.Disabled) { srcColorBlend.floatValue = (float)BlendMode.SrcAlpha; dstColorBlend.floatValue = (float)BlendMode.OneMinusSrcAlpha; srcAlphaBlend.floatValue = (float)BlendMode.One; dstAlphaBlend.floatValue = (float)BlendMode.OneMinusSrcAlpha; } else if (blendMode == FullscreenBlendMode.Premultiply) { srcColorBlend.floatValue = (float)BlendMode.One; dstColorBlend.floatValue = (float)BlendMode.OneMinusSrcAlpha; srcAlphaBlend.floatValue = (float)BlendMode.One; dstAlphaBlend.floatValue = (float)BlendMode.OneMinusSrcAlpha; } else if (blendMode == FullscreenBlendMode.Additive) { srcColorBlend.floatValue = (float)BlendMode.SrcAlpha; dstColorBlend.floatValue = (float)BlendMode.One; srcAlphaBlend.floatValue = (float)BlendMode.One; dstAlphaBlend.floatValue = (float)BlendMode.One; } else if (blendMode == FullscreenBlendMode.Multiply) { srcColorBlend.floatValue = (float)BlendMode.DstColor; dstColorBlend.floatValue = (float)BlendMode.Zero; srcAlphaBlend.floatValue = (float)BlendMode.One; dstAlphaBlend.floatValue = (float)BlendMode.OneMinusSrcAlpha; } colorBlendOp.floatValue = (float)BlendOp.Add; alphaBlendOp.floatValue = (float)BlendOp.Add; } } /// /// Draw the Surface Inputs section of the fullscreen shader GUI. /// /// A valid material using a Fullscreen Shader Graph. protected virtual void DrawSurfaceInputs(Material material) { DrawShaderGraphProperties(m_MaterialEditor, material, m_Properties); } static void DrawShaderGraphProperties(MaterialEditor materialEditor, Material material, MaterialProperty[] properties) { if (properties == null) return; ShaderGraphPropertyDrawers.DrawShaderGraphGUI(materialEditor, properties); } /// /// Ensures that the material is correctly setup. /// /// A valid material using a Fullscreen Shader Graph. public override void ValidateMaterial(Material material) => SetupSurface(material); /// /// Setup the fullscreen shader keywords from the material properties. /// /// A valid material using a Fullscreen Shader Graph. public static void SetupSurface(Material material) { // For now there is no keyword in FullScreenShader. } } }