在Unity中可以通过#pragma multi_compile
或者#pragma shader_feature
指令来实现着色器多样化。
在运行时,相应的着色器变体是从材质的关键词中取得的(Material.EnableKeyword和 DisableKeyword),或者全局着色器关键词(Shader.EnableKeyword和 DisableKeyword)。
0.如何查看shader使用了多少变体?
在shader的inspector面板中可以看到shader的Compiled code选项,点开之后有一个变体数量查看。
1.multi_compile的用法简析
如果使用了下面的语法,也就表示定义了两个变体,TEST_OFF和TEST_ON。在运行时,其中的一个将被激活,根据材质或者全局着色器关键词(#ifdef TEST_OFF之类的宏命令也可以)来确定激活哪个。若两个关键词都没有启用,那么将默认使用前一个选项,也就是关闭(OFF)的选项TEST_OFF。
#pragma multi_compile TEST_OFF TEST_ON
当#pragma multi_compile中存在所有名字都是下划线的一个指定段时,就表示产生一个空的着色器变种。这种做法在着色器编写中比较常见,因为这样可以节省了一个变量个数的占用(下面会提到,Unity中关键词个数是有129个的数量限制的)。例如,下面的指令将产生两个着色器变体;第一个没有定义,第二个定义为TEST_ON:
#pragma multi_compile __ TEST_ON
我们也可以同时创建多个变体,就像下面这样:
#pragma multi_compile TEST_A TEST_B TEST_C
此外,我们还可以使用多行指令对变体进行组合,但是这样做的话,会导致变体数量成倍的增长,如果使用下面的代码生成变体,那么我们会得到3*2=6种不同的变体:
#pragma multi_compile TEST_A TEST_B TEST_C
#pragma multi_compile TEST_D TEST_E
2.shader_feature的用法简析
shader_feature的用法与multi_compile大致相同,唯一的区别在于shader_feature不会将不用的shader变体添加到程序中去。shader_feature更适用于材质的关键字,而multi_compile更适用于代码设置的全局关键字。
下面两种代码是一样的,第一行是第二行代码的简略版,效果是相同的。
#pragma shader_feature TEST_A
#pragma shader_feature __ TEST_A
这里我们明明声明了TEST_A变体,但是实际上变体列表中却什么都没有,这是因为我们在查看的时候忽略掉了没有用到的变量:
那么我们该怎么启用对应的变体呢?在运行时,相应的着色器变体是从材质的关键词中取得的(Material.EnableKeyword和 DisableKeyword),或者全局着色器关键词(Shader.EnableKeyword和 DisableKeyword)。
所以我们添加以下代码,并将测试用的材质放在上面,这个时候在运行游戏,我们可以在frame debugger中查看渲染时shader使用的变体,也可以在之前的inspector面板上查看现在拥有的变体数量:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class test : MonoBehaviour {
public Material mat;
// Use this for initialization
void Start()
{
mat.EnableKeyword("TEST_A");
}
}
3.unity内置的变体
multi_compile_fwdbase
此指令表示,编译正向基础渲染通道(用于正向渲染中,应用环境光照、主方向光照和顶点/球面调和光照(Spherical Harmonic Lighting))所需的所有变体。这些变体用于处理不同的光照贴图类型、主要方向光源的阴影选项的开关与否。
multi_compile_fwdadd
此指令表示, 编译正向附加渲染通道(用于正向渲染中;以每个光照一个通道的方式应用附加的逐像素光照)所需的所有变体。这些变体用于处理光源的类型(方向光源、聚光灯或者点光源),且这些变种都包含纹理cookie。
multi_compile_fwdadd_fullshadows
此指令和上面的正向渲染附加通道基本一致,但同时为上述通道的处理赋予了光照实时阴影的能力。
multi_compile_fog
此指令表示,编译出几个不同的Shader变体来处理不同类型的雾效(关闭/线性/指数/二阶指数)(off/linear/exp/exp2).
此外,大部分内建快捷写法会导致许多shader变体,如果某些不需要使用,那么可以使用#pragma skip_variants来忽略它们:
#pragma multi_compile_fwdadd
// will make all variants containing
// "POINT" or "POINT_COOKIE" be skipped
#pragma skip_variants POINT POINT_COOKIE
注:Unity中最多可以有256个全局关键字,但是Unity自己已经定义了一部分了(预留了大概60个左右内部使用),所以实际上会更少。所以使用multi_compile和shader_feature的时候,就需要特别注意不要超过这个限制。
番外.关于local keyword
以下的内容在2019.1版本的文档中更新,所以2019之前版本是不支持local keyword的,我还傻傻的用我的2017.4试了一下。结果并不能生成keyword....
为了解决keyword的问题,unity现在除了支持256个全局keyword以外,还支持64个local keyword。所以我们现在还可以使用shader_feature_local
和 multi_compile_local
指令来生成local keyword满足我们的需求。使用local keyword有助于提高项目的性能,同时也可以减少每个shader中keyword数量。如果global keyword 与local keyword重名的话,unity会优先使用local keyword。
但是同时,local keyword 与global keyword相比还有以下的几点限制:
1.不能将local keyword与global keyword更改的API一起使用(如Shader.EnableKeyword和CommandBuffer.EnableShaderKeyword)(可能指的是重名关键字?或者是不能用全局API来改变local keyword?)
2.每个shader最多只能使用64个local keyword
3.如果材质使用了一个local keyword并且启用了这个keyword,然后它的shder替换成了一个没有声明这个local keyword的shader,unity会声明一个global keyword
参考文章:
https://blog.csdn.net/poem_qianmo/article/details/49719247
https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html
https://gameinstitute.qq.com/community/detail/121999