unity-shader-ShaderVariant变体及宏及材质编辑器扩展

unity-shader-ShaderVariant变体及宏及材质编辑器扩展


前篇


说明

#pragma shader_feature#pragma multi_compile 相似, 都是起到宏的作用

不同的是 shader_feature 没有用到的不会被包含进去, 而 multi_compile 全部版本都会被包含

所以 shader_feature 材质用, multi_compile 代码控制用.

一般是写 XXX_OFFXXX_ON

另外, 使用 shader_feature 时也极其容器踩坑, 参考 : 踩坑

变体计算

选中 shader, 可以通过减少 #pragma shader_feature#pragma multi_compile 声明的宏, 开查看. 可以看到是成倍的增减

变体的计算, 假设原来初始有 23 个, 加入以下宏申明 ( 假设所有的 shader_feature 都被使用了 )

1
2
3
4
#pragma shader_feature CS_BOOL
#pragma shader_feature _BLENDMAP
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
#pragma multi_compile _OVERLAY_NONE2 _OVERLAY_ADD2

总量 = 23 * ( 2 * 3 * 2 ) = 276

使用情况

(非常重要!!!),shader_feature 适用于那些会在材质面板中直接设置的情况,而如果需要在脚本里通过DisableKeyword 和 EnableKeyword 来开启或关闭 keyword 的话就应该使用 multi_compile。(栽过坑啊!!!)并且不要在#pragma后面写注释!!!如果要在脚本里通过 Shader.DisableKeyword 来控制 keyword 的开关的话,不要在 Properties 里写 KeywordEnum,这样可能会导致脚本里的设置失效(可以重新建一个材质解决)。但如果是使用 material.DisableKeyword 来设置的话,就不会有这个问题,原因暂时不明。


材质面板扩展

shader 定义材质面板

类别 描述
ToggleDrawer 把一个类型为float的属性显示为一个开关,它的值要么是0要么是1。当选中它时,Unity还会设置一个名为大写属性名_ON(可以自定义名字)的shader feature(需要在shader里使用”#pragma shader_feature”来定义它),我们可以在shader里用过#if、#ifdef或者#if defined关键词来判断它当前是否被开启。
EnumDrawer 把一个类型为float的属性显示为一个下拉列表。可以使用UnityEngine.Rendering命名空间下的各种状态来设置对应的渲染状态,例如ZWrite、ZTest、Blend等。据实验推测,这个值不可以随便定义,老老实实用UnityEngine.Rendering。
KeywordEnum 和EnumDrawer类似,也会把一个类型为float的属性显示为一个下拉列表,但不同的是它会定义一组shader keyword(需要在shader里使用”#pragma multi_compile”来定义它们)。这些keyword的名字是大写属性名_枚举名。同样,我们可以在shader里用过#if、#ifdef或者#if defined配合#elif来判断当前选择是哪个keyword。最多同时支持9个keywords。
PowerSliderDrawer 显示一个非线性响应的滑动条,其中PowerSliderDrawer中的参数指定了底数,然后我们再根据Range()来指定范围。其实简单说来就是滑动条上的数值不再是均匀变化了,而是xsliderxslider进行变化,但我们在shader里还是可以直接访问到一个float数值。
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
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
}
}
}

cs 脚本扩展材质面板

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
using UnityEngine;
using UnityEditor;
using System;

public class TestShaderGUI : ShaderGUI
{
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
base.OnGUI(materialEditor, properties); // 显示默认面板

Material targetMat = materialEditor.target as Material;

// test1, shader 中增加一个 Toggle, 来决定编辑器是否显示 _Color2 属性
MaterialProperty _UseTwoColors = ShaderGUI.FindProperty("_UseTwoColors", properties);
if (_UseTwoColors.floatValue == 1)
{
MaterialProperty _Color2 = ShaderGUI.FindProperty("_Color2", properties);
materialEditor.ShaderProperty(_Color2, _Color2.displayName);
}

// test2, 检测贴图是否有被用到, 来决定是否启用 _BLENDMAP 宏
MaterialProperty blendMap = ShaderGUI.FindProperty("_BlendTex", properties);
bool blendEnabled = blendMap.textureValue != null;
if (blendEnabled) {
targetMat.EnableKeyword("_BLENDMAP");
}
else {
targetMat.DisableKeyword("_BLENDMAP");
}

// test3, 编辑器扩展一个 Toggle, 来决定是否启用 CS_BOOL 宏
bool CS_BOOL = Array.IndexOf(targetMat.shaderKeywords, "CS_BOOL") != -1;
EditorGUI.BeginChangeCheck();
CS_BOOL = EditorGUILayout.Toggle("CS_BOOL", CS_BOOL);

if (EditorGUI.EndChangeCheck())
{
if (CS_BOOL)
targetMat.EnableKeyword("CS_BOOL");
else
targetMat.DisableKeyword("CS_BOOL");
}
}
}


踩坑