Unity Material Property Drawer – 客制化材质编辑器

发表于2017-03-16
评论0 3.7k浏览
材质球 Material,是在 Unity 中调整画面呈现的一个必备元素,透过材质球可以相当轻易的达到可见即所得的效果,下面就给大家介绍下Unity中的客制化材质编辑器

在 Unity 环境下撰写 Shader 后,会透过材质球选取我们所完成的 Shader 并且在 Inspector 中显示可调整的参数,但是这些参数往往无法满足我们的需求,这时候就会针对特定需求来进行调整。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Shader "Custom/Normal"
{
    Properties
    {
        _Int("Int", Int) = 1
        _Range("Range", Range(0, 1)) = 1
        _Color("Color", Color) = (1, 1, 1, 1)
        _Vector("Vector", Vector) = (1, 1, 1, 1)
        _Cube("Cube", Cube) = "white" {}
        _2D("2D", 2D) = "white" {}
        _3D("3D", 3D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _Color;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

MaterialPropertyDrawer

为 Unity ShaderLab 中的一种扩充语法(Syntax),用法相当的轻便且容易上手,可以透过这种语法方便的进行材质编辑器的扩充,也是这次要探讨的主题。在 MaterialPropertyDrawer 中,Unity 提供了以下几种扩充语法 Header、Space、Toggle、Enum、KeywordEnum、PowerSlider 以及 IntRange,下面就来依次说明。

PS: 本次的环境为 Unity 5.5.0f3,所有说明皆为用法范例,并无特殊功能。

 

Header

与 HeaderAttribute 一样,透过包装宣告参数,来达到编排版面的作用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Shader "Custom/Header"
{
    Properties
    {
        [Header(This is the first Header)]
        _FirstColor("First Color", Color) = (1, 1, 1, 1)
  
        [Header(This is the second Header)]
        _SecondColor("Second Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
  
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _FirstColor;
            float4 _SecondColor;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _FirstColor + _SecondColor;
  
                return col;
            }
            ENDCG
        }
    }
}

Space

与 SpaceAttribute 一样,在两参数之间安插任意垂直空间,来达到编排版面的作用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
Shader "Custom/Space"
{
    Properties
    {
        [Header(This is the first Header)]
        _FirstColor("First Color", Color) = (1, 1, 1, 1)
  
        [Space]
  
        [Header(This is the second Header)]
        _SecondColor("Second Color", Color) = (1, 1, 1, 1)
  
        [Space(50)]
  
        [Header(This is the third Header)]
        _ThirdColor("Third Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
  
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _FirstColor;
            float4 _SecondColor;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _FirstColor + _SecondColor;
  
                return col;
            }
            ENDCG
        }
    }
}
Toggle

以触发器型态表示浮点数,可以用来处理开关变换的过程,需要注意的是,在宣告参数 _Invert 后,必须额外使用 #pragma shader_feature 定义 _INVERT_ON(大写参数名 + _ON)来作为配套使用,这边就用简单的负片效果当作示范。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Shader "Custom/Toggle"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
        [Toggle] _Invert("Invert?", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
            #pragma shader_feature _INVERT_ON
  
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _Color;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _Color;
  
                #if _INVERT_ON
                col = 1 - col;
                #endif
  
                return col;
            }
            ENDCG
        }
    }
}
Enum

Enum 是一般在撰写 Scripts 时常常见到的功能,而在 ShaderLab 中也一样支援了这个语法,这边使用切换 Culling Mode 来当作范例。透过这种切换方式,就能够单纯地利用 Material 的生成,来切换各种不同型态的 Culling Mode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Shader "Custom/Enum"
{
    Properties
    {
        _Color("Color", Color) = (1, 1, 1, 1)
        [Enum(UnityEngine.Rendering.CullMode)] _CullMode ("Cull Mode", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            Cull [_CullMode]
  
            CGPROGRAM
  
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _Color;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _Color;
  
                return col;
            }
            ENDCG
        }
    }
}

KeywordEnum

与 Enum 相似,可以自定义枚举名称,需要注意在参数宣告后,需与 #pragma multi_compile 配套使用(大写参数名 + 大写枚举名)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Shader "Custom/KeywordEnum"
{
    Properties
    {
        [KeywordEnum(Red, Green, Blue)] _ColorMode ("Color Mode", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
            #pragma multi_compile _COLORMODE_RED _COLORMODE_GREEN _COLORMODE_BLUE
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = fixed4(0, 0, 0, 1);
  
                #if _COLORMODE_RED
                col.r = 1;
                #elif _COLORMODE_GREEN
                col.g = 1;
                #elif _COLORMODE_BLUE
                col.b = 1;
                #endif
  
                return col;
            }
            ENDCG
        }
    }
}

PowerSlider

使用上与原始的 Range(min, max) 大同小异,唯一的差别是滑动条上的数值不再是线性变化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Shader "Custom/PowerSlider"
{
    Properties
    {
        [PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
  
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _Color;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _Color;
  
                return col;
            }
            ENDCG
        }
    }
}
IntRange

使用上与原始的 Range(min, max) 大同小异,唯一的差别是滑动条上的数值为整数变化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Shader "Custom/IntRange"
{
    Properties
    {
        [IntRange] _Alpha ("Alpha", Range (0, 255)) = 100
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Pass
        {
            CGPROGRAM
  
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _Color;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _Color;
  
                return col;
            }
            ENDCG
        }
    }
}

结语

透过 MaterialPropertyDrawer 虽然可以相当简单的製作出属于自己的材质编辑器,但製作简单也就意味著扩充性的不足,单独依靠 MaterialPropertyDrawer 依然没有办法拥有完整的控制权,若是需求特殊时,仍然必须使用 MaterialEditor 来作为首要选择。但在无特别开发需求的状况下,仍然可以透过 MaterialPropertyDrawer 来节省大量开发时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
Shader "Custom/Customize"
{
    Properties
    {
        [Header(Toggle)]
        [Toggle] _Invert("Invert?", Float) = 0
        [Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
  
        [Header(KeywordEnum)]
        [KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
        [Space]
  
        [Header(PowerSlider)]
        [PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08
        [Space]
  
        [Header(IntRange)]
        [IntRange] _Alpha ("Alpha", Range (0, 255)) = 100
  
        [Header(Cull)]
        [Enum(UnityEngine.Rendering.CullMode)] _CullMode ("Cull Mode", Float) = 0
        [Space]
  
        [Header(Blend)]
        [Enum(UnityEngine.Rendering.BlendMode)] _BlendSrcFactor ("Blend SrcFactor", Float) = 0
        [Enum(UnityEngine.Rendering.BlendMode)] _BlendDstFactor ("Blend DstFactor", Float) = 0
  
        [Header(Stencil)]
        [IntRange] _StencilRef ("Stencil Reference", Range(0, 255)) = 0
        [Enum(CompareFunction)] _StencilComp ("Stencil Compare Function", Float) = 0
        [Enum(UnityEngine.Rendering.StencilOp)] _StencilOp ("Stencil Operation", Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
  
        Stencil
        {
            Ref [_StencilRef]
            Comp [_StencilComp]
            Pass [_StencilOp]
        }
  
        Pass
        {
            Cull [_CullMode]
            Blend [_BlendSrcFactor] [_BlendDstFactor]
  
            CGPROGRAM
            #pragma shader_feature _INVERT_ON
            #pragma shader_feature ENABLE_FANCY
            #pragma vertex vert
            #pragma fragment frag
              
            #include "UnityCG.cginc"
  
            struct appdata
            {
                float4 vertex : POSITION;
            };
  
            struct v2f
            {
                float4 vertex : SV_POSITION;
            };
  
            float4 _Color;
              
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
  
                return o;
            }
              
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = _Color;
  
                #if _INVERT_ON
                col = 1 - col;
                #endif
  
                #if ENABLE_FANCY
                col.r = 0.5;
                #endif
  
                return col;
            }
            ENDCG
        }
    }
}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引