Unity Shader笔记(二)

在使用shader时,可能会遇到这样的情况:某个shader的绝大部分代码适用于所有的情况,但是有少部分内容需要根据具体的情况采用不同的代码。此时会需要保持shader的大部分代码固定不变,得到对少部分代码做轻微的改动的shader变体。multi_compile用于产生多种不同的shader变体。multi_compile会根据不同的情况,使用不同的预处理器指令,多次编译shader代码。

multi compile

在ShaderLab中使用下边的方式定义multi_compile,然后即可在材质面板(配合TOGGLE)或者代码中控制开启或禁用该关键字(ShaderMaterialEnableKeywordDisableKeyword方法):

1
#pragma multi_compile XXX

一个示例:

1
#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON

将会产生两个shader变体,分别是定义了FANCY_STUFF_OFFFANCY_STUFF_ON。在运行时,会根据材质或shader激活的关键字来启用其中之一。如果二者均未被激活则启用第一个(off)。也可以定义大于两个的关键字,如:

1
#pragma multi_compile SIMPLE_SHADING BETTER_SHADING GOOD_SHADING BEST_SHADING

将会产生4个shader变体。

当一个关键字全部由下划线组成时,则会生成一个不包含任何预定义宏的变体,如:

1
#pragma multi_compile __ FOO_ON

将会生成两个shader变体,其中一个不包含预定义宏,另一个包含FOO_ON。这种用法通常是配合shader_feature(后边提到)使用,以减少使用的关键字数量,Unity中支持使用的关键字的数量是有限的。

shader feature

multi_compile功能相似。需要注意的是,对于shader_feature,在游戏中没有使用的关键字,在构建游戏时不会生成对应的shader变体。也就是说如果是在运行时才用代码去开启和关闭关键字(Enable/DisableKeyword)的话,需要使用multi_compile

在使用shader_feature时,有一种简便写法,即当只有一个关键字时,以下的两种写法是等效的:

1
2
#pragma shader_feature FANCY_STUFF
#pragma shader_feature _ FANCY_STUFF

multi compile的组合

一个示例:

1
2
#pragma multi_compile A B C
#pragma multi_compile D E

会生成6种变体,分别是A+DB+DC+DA+EB+EC+E

内置multi compile与忽略

  • multi_compile_fwdbase:编译ForwardBase(forward rendering base)所需的所有变体。这些变体用于处理不同的lightmap类型,及主方向光是否产生阴影。

  • multi_compile_fwdadd:编译ForwardAdd(forward rendering additive)所需的所有变体。这些变体用于处理方向光、聚光灯及点光源类型,以及使用剪影(cookie textures)的变体。

  • multi_compile_fwdadd_fullshadows:和上边的相同,还包括了实时阴影的光照。

  • multi_compile_fog:用于处理不同类型的雾的变体(off/linear/exp/exp2)。

绝大多数内置的快捷操作会产生很多的shader变体。如有需要可以使用#pragma skip_variants来忽略其中的部分变体,如:

1
2
3
4
#pragma multi_compile_fwdadd
// will make all variants containing
// "POINT" or "POINT_COOKIE" be skipped
#pragma skip_variants POINT POINT_COOKIE

Shader GUI

Toggle

在编辑器UI上绘制Toggle,并且将其与multi_compile关联。定义float属性,标为Toggle,与其关联的multi_compile的关键字是属性名的全大写形式加上_ON,如:

1
[Toggle] _Maskable ("Maskable?", Float) = 1

或者直接指明:

1
[Toggle(_MASKABLE_ON)] _Maskable ("Maskable?", Float) = 1

shader中直接使用:

1
2
3
4
5
6
7
8
9
#pragma multi_compile _ _MASKABLE_ON

// ...

#ifdef _MASKABLE_ON

// do something

#endif

自定义GUI

要使用自定义的GUI,需要在shader的末尾指定:

1
2
3
4
5
6
7
Shader "TestShader"
{
{
// ...
}
CustomEditor "TestShaderGUI"
}

定义的自定义GUI类,主要是需要覆写OnGUI方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEditor;
public class TestShaderGUI : ShaderGUI {

override public void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
base.OnGUI(materialEditor, properties);

Material material = materialEditor.target as Material;

MaterialProperty extraTexture = FindProperty("_ExtraTexture", properties);

// ...
}
}

REFERENCE

http://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html

https://docs.unity3d.com/Manual/SL-CustomShaderGUI.html

https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html

https://blog.csdn.net/candycat1992/article/details/51417965