之前遇到过一些朋友问怎么在材质面板里定义类似于bool这种变量,控制一些代码的执行。我们当然可以写一个C#文件来自定义材质面板,就像Unity为Standard Shader写材质面板一样(你可以在built-in shader包里找到这些文件),但这样有时候太麻烦了。实际上,有一种更简单的方法,不需要写额外的C#文件就可以直接在shader里定义类似bool、enum这种简单的变量,Unity为这些变量提供了内置的材质面板。而这就是通过MaterialPropertyDrawer(当然我们也可以重写内置的MaterialPropertyDrawer,但太麻烦我就不写了)。
示例总体来说,MaterialPropertyDrawers内置了4种最常见的面板:ToggleDrawer,EnumDrawer,KeywordEnumDrawer,PowerSliderDrawer。
下面的代码没有任何实际意义,只是为了演示四种面板:
-
Shader "Custom/Material Property Drawer Example"
-
{
-
Properties
-
{
-
// Header creates a header text before the shader property.
-
[Header(Material Property Drawer Example)]
-
// Space creates vertical space before the shader property.
-
[Space]
-
-
_MainTex ("Main Tex", 2D) = "white" {}
-
_SecondTex ("Second Tex", 2D) = "white" {}
-
-
// Large amount of space
-
[Space(50)]
-
-
// Toggle displays a **float** as a toggle.
-
// The property value will be 0 or 1, depending on the toggle state.
-
// When it is on, a shader keyword with the uppercase property name +"_ON" will be set,
-
// or an explicitly specified shader keyword.
-
[Toggle] _Invert ("Invert color?", Float) = 0
-
-
// Will set "ENABLE_FANCY" shader keyword when set
-
[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
-
-
// Enum displays a popup menu for a **float** property.
-
// You can supply either an enum type name
-
// (preferably fully qualified with namespaces, in case there are multiple types),
-
// or explicit name/value pairs to display.
-
// Up to **7** name/value pairs can be specified
-
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend Mode", Float) = 1
-
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend Mode", Float) = 1
-
[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0
-
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 0
-
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1
-
-
// KeywordEnum displays a popup menu for a **float** property, and enables corresponding shader keyword.
-
// This is used with "#pragma multi_compile" in shaders, to enable or disable parts of shader code.
-
// Each name will enable "property name" + underscore + "enum name", uppercased, shader keyword.
-
// Up to **9** names can be provided.
-
[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
-
-
// PowerSlider displays a slider with a non-linear response for a Range shader property.
-
// A slider with 3.0 response curve
-
[PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08
-
}
-
SubShader
-
{
-
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
-
Blend [_SrcBlend] [_DstBlend]
-
ZWrite [_ZWrite]
-
ZTest [_ZTest]
-
Cull [_Cull]
-
-
Pass
-
{
-
CGPROGRAM
-
// Need to define _INVERT_ON shader keyword
-
#pragma shader_feature _INVERT_ON
-
// Need to define _INVERT_ON shader keyword
-
#pragma shader_feature ENABLE_FANCY
-
// No comma between features
-
#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 _Shininess;
-
-
struct appdata
-
{
-
float4 vertex : POSITION;
-
float2 uv : TEXCOORD0;
-
};
-
-
struct v2f
-
{
-
float4 uv : TEXCOORD0;
-
float4 vertex : SV_POSITION;
-
};
-
-
v2f vert (appdata v)
-
{
-
v2f o;
-
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
-
o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
-
o.uv.zw = TRANSFORM_TEX(v.uv, _SecondTex);
-
return o;
-
}
-
-
fixed4 frag (v2f i) : SV_Target
-
{
-
// sample the texture
-
fixed4 col = tex2D(_MainTex, i.uv.xy);
-
-
// Use #if, #ifdef or #if defined
-
#if _INVERT_ON
-
col = 1 - col;
-
#endif
-
-
// Use #if, #ifdef or #if defined
-
#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 *= _Shininess;
-
-
return col;
-
}
-
ENDCG
-
}
-
}
-
}
- 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
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
得到的面板是这样的:
上面的代码基本显示了所有可能的例子。有一点需要注意的是使用#pragma multi_compile声明多个keyword时,每个keyword之间是没有逗号的,而官网里的有逗号,这是不对的。
除了上面的四种内置属性面板外,还有一些装饰性的内置drawer,例如Space和Header。在上面的例子里,我们在它们的后面直接写属性,像下面这样:
[Space] _Prop1 ("Prop1", Float) = 0
- 1
装饰性drawer的后面有没有紧跟属性都是可以的。
shader feature和shader keyword从上面的内容,我们知道可以使用#pragma shader_feature和#pragma multi_compile来实现“只写一份代码但实现不同功能”的目的。但世界上没有免费的午餐,Unity实际上只是在背后生成了很多份有少许不同的shader变种(shader variants),每个变种包含了不同的keyword而已,没什么非常神奇的地方。
当我们在代码里写到:
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
- 1
Unity就生成了3个shader变种,每个对应开启其中的一个keyword。
如果我们再添加新的定义:
#pragma shader_feature ENABLE_FANCY
- 1
Unity会再分别为是否开启ENABLE_FANCY来生成两种变种,也就是一共生成了3*2=6个shader变种。因此,当我们定义了大量的feature或者keyword的时候,就会出现有大量的变种!(而且Unity允许的keyword数目也是有限制的。)
#pragma shader_feature和#pragma multi_compile的区别那么#pragma shader_feature和#pragma multi_compile有什么区别呢?实际上,#pragma shader_feature是#pragma multi_compile的子集,#pragma shader_feature生成的变种一个是不包含任何keyword的,一个是包含某个keyword的。我们可以使用#pragma multi_compile来实现同样的目的,只需要使用一个全是下划线的名字来表示不使用任何keyword即可。下面的两句话是在大多数情况下等价的:
-
#pragma shader_feature ENABLE_FANCY
-
#pragma multi_compile __ ENABLE_FANCY
- 1
- 2
但区别在于,使用multi_compile来定义keyword的话Unity是一定会生成所有变种的,。而如果使用的是shader_feature的话,如果有些keyword我们实际上并没有使用到,Unity也不会为这些生成shader变种,我们可以在shader的代入面板里看到到底实际生成了多少个变种:
因此(非常重要!!!),shader_feature适用于那些会在材质面板中直接设置的情况,而如果需要在脚本里通过DisableKeyword和EnableKeyword来开启或关闭keyword的话就应该使用multi_compile。(栽过坑啊!!!)并且不要在#pragma后面写注释!!!如果要在脚本里通过Shader.DisableKeyword来控制keyword的开关的话,不要在Properties里写KeywordEnum,这样可能会导致脚本里的设置失效(可以重新建一个材质解决)。但如果是使用material.DisableKeyword来设置的话,就不会有这个问题,原因暂时不明。
实际上,Unity的surface shader能够有那样强大的功能也是依靠了这样的机制。也包括我们在通过使用#pragma multi_compile_fwdbase或multi_compile_fwdadd这样的代码时,我们之所以需要使用这样的语句就是因为Unity为forward pass等内置了一些shader keyword,而我们通过这些语句来让unity为不同的光照pass生成不同的shader变种,否则的话我们的shader就一种类型,只能包含特定的任务,无法为多类型的光源等进行不同的服务。
当然,我们可以独立使用#pragma shader_feature和#pragma multi_compile,而不必一定要和MaterialPropertyDrawer配合使用。我们可以直接在代码里通过Material.EnableKeyword和Material.DisableKeyword来局部开启某些keyword,也可以通过Shader.EnableKeyword和Shader.DisableKeyword来全局开启。
参考链接:http://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html
转载于:https://www.cnblogs.com/xiaowangba/p/6314638.html