首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之材质Shader Graph通用面板(一百二十三)
2020
12-29

Unity3D研究院之材质Shader Graph通用面板(一百二十三)

如果是自定义shader的通用面板可以看我之前的文章 Unity3D研究院之材质Shader通用面板(一百二十一) 这篇文章和它的原理是一样的。

首先shader graph有个很麻烦的问题就是自定义的keyword始终在面板的最下面不能和普通的属性进行排序,而且也无法展开分组的功能。为了实现通用面板,我们要定义一定的规则

XX面板        溶解面板

XX参数1      溶解参数1

XX参数2      溶解参数2

如果命名中包含面板会特意提出来,如果下面的属性包含XX部分则合并在一个分组中,自定义的keyword始终在最下面的问题,有可以根据命名的规则来进行排序,我会始终让宏在分页的第一位。

Snip20201229_2

 

Snip20201229_3
最后上代码

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

public class CustomShaderGraphGUI : ShaderGUI
{
    public class GraphData
    {
        public string groupName; //组名
        public MaterialProperty title; //标题
        public List<GraphData> child;//表示有子节点
    }
    static Dictionary<string, GraphData> s_GraphProperty = new Dictionary<string, GraphData>();
    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
    {
        Shader shader = (materialEditor.target as Material).shader;
        s_GraphProperty.Clear();
        for (int i = 0; i < properties.Length; i++)
        {
            var propertie = properties[i];
            var displayName = propertie.displayName;
            GraphData data = new GraphData() { title = propertie };
            if (TryGetGroupName(displayName, out var groupName))
            {
                if (!s_GraphProperty.TryGetValue(groupName, out var graph))
                {
                    data.child = new List<GraphData>();
                    data.groupName = groupName;
                    s_GraphProperty[groupName] = data;
                }
                else
                {
                    var attributes = shader.GetPropertyAttributes(i);
                    bool keyword = Array.FindIndex(attributes, (t) => (t == "Toggle" || t.StartsWith("KeywordEnum"))) >= 0;
                    if (keyword)
                        graph.child.Insert(0, data);
                    else
                        graph.child.Add(data);
                }
            }
            else
            {
                s_GraphProperty[displayName] = data;
            }
        }
        PropertiesDefaultGUI(materialEditor);
    }

    private static int s_ControlHash = "EditorTextField".GetHashCode();
    public void PropertiesDefaultGUI(MaterialEditor materialEditor)
    {
        var f = materialEditor.GetType().GetField("m_InfoMessage", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        if (f != null)
        {
            string m_InfoMessage = (string)f.GetValue(materialEditor);
            materialEditor.SetDefaultGUIWidths();
            if (m_InfoMessage != null)
            {
                EditorGUILayout.HelpBox(m_InfoMessage, MessageType.Info);
            }
            else
            {
                GUIUtility.GetControlID(s_ControlHash, FocusType.Passive, new Rect(0f, 0f, 0f, 0f));
            }
        }

        foreach (var props in s_GraphProperty.Values)
        {
            MaterialProperty prop = props.title;
            if ((prop.flags & (MaterialProperty.PropFlags.HideInInspector | MaterialProperty.PropFlags.PerRendererData)) == MaterialProperty.PropFlags.None)
            {
                if(props.child!=null && props.child.Count > 0)
                {
                    //如果发现有面板,使用Foldout来绘制
                    prop.floatValue = Convert.ToSingle(EditorGUILayout.Foldout(Convert.ToBoolean(prop.floatValue), prop.displayName));
                    if (prop.floatValue == 1f)
                    {
                        foreach (var child in props.child)
                        {
                            DrawGUI(materialEditor, child.title, true);
                        }
                    }
                }
                else
                {
                    DrawGUI(materialEditor, prop, false);
                }
            }
        }

        EditorGUILayout.Space();
        EditorGUILayout.Space();
        if (SupportedRenderingFeatures.active.editableMaterialRenderQueue)
        {
            materialEditor.RenderQueueField();
        }
        materialEditor.EnableInstancingField();
        materialEditor.DoubleSidedGIField();
        //unity 2020 新版本功能 ,老版本需要注释掉
        materialEditor.EmissionEnabledProperty();
    }

    void DrawGUI(MaterialEditor materialEditor, MaterialProperty prop, bool indentLevel)
    {
        float propertyHeight = materialEditor.GetPropertyHeight(prop, prop.displayName);
        Rect controlRect = EditorGUILayout.GetControlRect(true, propertyHeight, EditorStyles.layerMaskField);
        //如果是面板下面的元素统一向右缩进,否则不向右缩进
        if (indentLevel) EditorGUI.indentLevel++;
        materialEditor.ShaderProperty(controlRect, prop, prop.displayName);
        if (indentLevel) EditorGUI.indentLevel--;
    }



    const string INSPECTOR_TXT = "面板";
    bool TryGetGroupName(string displayName,out string groupName)
    {
        //根据displayName找到组名
        if (displayName.Contains(INSPECTOR_TXT))
        {
            groupName = Regex.Split(displayName, INSPECTOR_TXT, RegexOptions.IgnoreCase)[0];
            return true;
        }
        foreach (var property in s_GraphProperty.Values)
        {
            if (!string.IsNullOrEmpty(property.groupName)){
                if (displayName.StartsWith(property.groupName))
                {
                    groupName = property.groupName;
                    return true;
                }
            }
        }
        groupName = string.Empty;
        return false;
    }
}

欢迎大家来测试

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

留下一个回复

你的email不会被公开。 必填项已用 * 标注。