Unity為用戶提供了專門快速實現(xiàn)自定義材質(zhì)面板的基礎類:MaterialPropertyDrawer。
1.0不同類型的DrawerClass
| 類型 | 描述 |
|---|---|
| ToggleDrawer | 將float類型數(shù)據(jù)顯示為開關,數(shù)值只能是0(關閉)或1(開啟) |
| EnumDrawer | 枚舉會將float型數(shù)據(jù)顯示為下拉列表,可以用于選擇混合系數(shù),比較方法等,也可以自定義 |
| KeywordEnumDrawer | 和EnumDrawer類似,但是需要先定義shader keyword才能使用 |
| PowerSliderDrawer | 指數(shù)對應關系的滑動條,滑動條上的數(shù)值不再按照線性關系進行對應 |
| IntRangeDrawer | 將范圍型的數(shù)據(jù)顯示為只能設置整數(shù)的滑動條 |
在編寫Shader的時候,DrawerClass需要寫在對應屬性之前的“[]”中,類別的后綴名稱“Drawer”不需要添加,因為Unity在編輯的時候會自動添加。
1.1 Toggle
將float類型的數(shù)據(jù)以開關的形式在材質(zhì)屬性面板上顯示,數(shù)值只能設置為0或1,0為關閉,1為開啟,Shader keyword會被Unity默認設置為“property name”+“_ON”,需要注意的一點是,關鍵詞的所有字母必須大寫。如:
// Shader的關鍵詞會被設置為_INVERT_ON
[Toggle] _Invert ("Invert color?", Float) = 0
除了使用Unity默認的關鍵詞,也可以自定義一個特殊的關鍵詞,如:
// ENABLE_FANCY即為自定義的Shader關鍵詞
[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
1.2 Enum
Enum將float類型的數(shù)據(jù)以下拉列表的形式在材質(zhì)面板上顯示,Unity為用戶提供了一些內(nèi)置的枚舉類,如BlendMode,CompareFunction。如:
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Src Blend Mode", Float)=1

用戶也可以自定義枚舉的名稱/數(shù)值對,但是一個枚舉最多只能自定義7個名稱/數(shù)值對。如:
[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0

1.3 KeywordEnum
KeywordEnum跟Enum類似,不過KeywordEnum會有與之對應的Shader keyword,在Shader中通過# pragma shader_feature或# pragma multi_compile指令可以或者關閉某一部分Shader代碼。
Shader keyword格式為:property name_enum name,屬性名稱+"下劃線"+枚舉名稱,所有英文必須大寫,并且最多支持9個關鍵詞。如:
// 對應的Shader keyword分別為:_OVERLAY_NONE,_OVERLAY_ADD,_OVERLAY_MULTIPLY
[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
1.4 PowerSlider
PowerSlider會將范圍型數(shù)值的屬性顯示為非線性對應的滑動條?;瑒訔l上的值不再按照線性關系進行對應,而是以指數(shù)的方式。如:
[PowerSlider(3.0)] _Brightness ("Brightness", Range (0.01, 1)) = 0.1
這是一個以3為指數(shù)對應關系的滑動條,括號內(nèi)的數(shù)值為指數(shù)。
曲線y=x^3,函數(shù)中的變量x就是滑動塊所在位置,y就是屬性的數(shù)值。

1.5 InRange
IntRange也是將數(shù)值以滑動條的形式在材質(zhì)屬性面板上顯示,只不過數(shù)值不再是float類型,只能是整數(shù)型數(shù)值,如:
[IntRange] _Alpha ("Alpha", Range (0, 255)) = 100
只能在滑動條上使用區(qū)間[0,255]之內(nèi)的整數(shù)值。

2.0 在編譯指令中定義Keyword
定義了ToggleDrawer或者KeywordEnumDrawer之后,如果想要正常使用,還需要在編譯指令中聲明Shader keyword。如:
#pragma shader_feature _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
或者
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
不同keyword之間需要用空格隔開。
雖然表面上看似通過一個Shader文件實現(xiàn)了不同種情況,但是Unity會自動將不同情況編譯成不同版本的Shader文件,這些不同版本的Shader文件被稱為Shader變體(Variants),上述編譯指令中包含三個Shader變體。
假設在添加一個指令:
#pragma shader_feature _INVERT_ON
本指令包含Toggle的關閉與開啟倆種情況,所以Unity最終會編譯出2x3=6個Shader變體。
倆種不同編譯指令之間的區(qū)別如下:
<1>shader_feature:只會為材質(zhì)使用到的keyword生成變體,沒有使用到的keyword不會生產(chǎn)變體,因此無法在運行的時候通過腳本切換效果。
<2>multi_compile:會為所有keyword生產(chǎn)變體,因此可以在運行的時候通過腳本切換效果。
在Shader文件的屬性設置面板中可以查看到本Shader生成的變體數(shù)量。
開啟Skip unused shader_features選項可以只查看使用keyword的變體數(shù)量。
關閉Skip unused shader_features選項查看所有keyword的變體數(shù)量。
如果需要確定具體變體有哪些keyword組成,可以單擊Show查看。

3.0 屬性的特性和Drawer
如果需要對開放出來的屬性進行一些限制,可以對屬性的特性和Drawer進行修改,這些修改命令需要寫在屬性語句之前。
| 指令 | 描述 |
|---|---|
| [HideInInspector] | 可以添加到任何Property之前,使屬性在材質(zhì)面板上隱藏 |
| [NoScaleOffset] | 添加在2D Property之前,可以在材質(zhì)面板上隱藏紋理貼圖的Tiling和Offset選項 |
| [Normal] | 添加在2D Property之前,可以檢測關聯(lián)的紋理貼圖是否為法線貼圖,如果不是,則會彈出修復提示 |
| [HDR] | 添加在2D或Color Property之前,可以是屬性開啟HDR效果,從而使數(shù)值突破1的限制,常用于自發(fā)光屬性 |
4.0 裝飾性 PropertyDrawer
裝飾性的PropertyDrawer只起到界面美觀作用,不會影響屬性本身。
| 類型 | 描述 |
|---|---|
| SpaceDecorator | 在材質(zhì)屬性面板上添加空白行 |
| HeaderDecorator | 在材質(zhì)屬性面板上添加標題文字 |
在編寫Shader的時候,類別的后綴名稱“Decorator”依然不需要添加,Unity在編譯的時候會自動添加。
4.1 SpaceDecorator
SpaceDecorator可以在屬性之前添加空白行,以起到分割屬性的作用。如:
[Space]_MainTex ("Main Tex", 2D) = "white" {}
可以在后邊直接寫上空白行的數(shù)量,如:
[Space(30)]_MainTex ("Main Tex", 2D) = "white" {}
4.2 HeaderDecorator
當Shader開放了很多屬性的時候,可以使用HeaderDecorator在屬性前添加一個標題文字,從而對不同類別的屬性進行區(qū)分。如:
[Header(Custom Material Inspector)]

5.0 完整PropertyDrawer示列
Shader "Custom/Custom Material Inspector"
{
Properties
{
// 在材質(zhì)面板插入一行標題
[Header(Custom Material Inspector)]
// 在材質(zhì)面板插入一行空白行,可以寫在單獨一行
[Space]
_MainTex ("Main Tex", 2D) = "white" {}
// 在材質(zhì)面板上隱藏Tiling和Offset
[NoScaleOffset] _SecondTex ("Second Tex", 2D) = "white" {}
// 在材質(zhì)面板插入30行空白行,可以寫在單獨一行
[Space(30)]
// 開關
[Toggle] _Invert ("Invert color?", Float) = 0
// 自定義Shader關鍵詞的開關
[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
// Unity內(nèi)置的枚舉下拉菜單
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Src Blend Mode", Float)=1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Dst Blend Mode", Float)=1
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 0
// 自定義枚舉下拉菜單
[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0
// 關鍵詞枚舉下拉菜單
[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
// 指數(shù)滑動條
[PowerSlider(3.0)] _Brightness ("Brightness", Range (0.01, 1)) = 0.1
// 整數(shù)滑動條
[IntRange] _Alpha ("Alpha", Range (0, 255)) = 100
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Blend [_SrcBlend] [_DstBlend]
Cull [_Cull]
ZTest [_ZTest]
ZWrite [_ZWrite]
Pass
{
CGPROGRAM
// 通過"#pragma shader_feature"定義 _INVERT_ON shader關鍵詞
#pragma shader_feature _INVERT_ON
// 通過"#pragma shader_feature"定義 ENABLE_FANCY shader關鍵詞
#pragma shader_feature ENABLE_FANCY
// 通過"#pragma multi_compile"定義關鍵詞枚舉的每一個shader關鍵詞
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SecondTex;
float4 _SecondTex_ST;
float _Brightness;
struct v2f
{
float4 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _SecondTex);
return o;
}
fixed4 frag (v2f i) : SV_TARGET
{
fixed4 col = tex2D(_MainTex, i.uv.xy);
// 通過 #if, #ifdef 或者 #if defined啟用某一部分代碼
#if _INVERT_ON
col = 1 - col;
#endif
#if ENABLE_FANCY
col.r = 0.5;
#endif
fixed4 secCol = tex2D(_SecondTex, i.uv.zw);
#if _OVERLAY_ADD
col += secCol;
#elif _OVERLAY_MULTIPLY
col *= secCol;
#endif
col *= _Brightness;
return col;
}
ENDCG
}
}
}
