着色器变体
汇总一下shader变体相关的内容
为什么要变体
最根本的原因是因为着色器在运行时产生分支的开销太大,有必要为了不同的条件产生不同的shader,减轻GPU的压力,用空间换时间。
主要是有以下几个目的
- 可以将同一着色器分配给不同的材质,然后为不同的材质配置不同的关键字,就可以做到在同一个地方维护代码,提高复用性。
- 可以在运行时通过开关关键字改变着色器行为。
- 利用变体禁用不必要的逻辑,优化运行效率,如没有法线贴图,那就没必要再去采用一次。
- 开关一些特定功能,如光照贴图等等
例如URP的lit就包含了一大堆关键字
指令
一般有4个
- multi_compile
- shader_feature
区别下文再详细描述
启用与禁用
- Shader.EnableKeyword:启用全局关键字(静态方法)
- Shader.DisableKeyword:禁用全局关键字(静态方法)
- CommandBuffer.EnableShaderKeyword:使用 CommandBuffer 来启用全局关键字
- CommandBuffer.DisableShaderKeyword:使用 CommandBuffer 来禁用全局关键字
- Material.EnableKeyword:为常规着色器启用本地关键字
- Material.DisableKeyword:为常规着色器禁用本地关键字
- ComputeShader.EnableKeyword:为计算着色器启用本地关键字
- ComputeShader.DisableKeyword:为计算着色器禁用本地关键字
其他条件判断
静态分支
编译时判断。
使用如用#if、#elif、#else和#endif预处理器指令,或#ifdef和#ifndef等,在编译时就确定的条件。
不影响性能。
动态分支
运行时判断。
可以使用关键字来作为动态分支的条件。
也可以单纯使用if来创建。
变体
根据条件来编译成多个着色器变体,在运行时选择与条件匹配的着色器程序。
关键字
可以不声明而直接使用关键字。但这样就不会产生对应的变体。
关键字集
一组关键字合称为关键字集。不同的关键字集合组合起来就会产生数量相乘的组合变体。
声明
声明时可以选择是用于变体还是动态分支。
- dynamic branch : 用于动态分支
- multi compile : 创建一组变体
- shader feature : 如果不启用就不会生成对应的变体
后两者顾名思义,前者是多重编译,即要为每种组合生成变体,而后者仅是开关着色器功能,如果编辑时不开启,那运行时也不会有。
范围
除了上面的编译时的区别,还有范围的区别。有局部和全局的差别。
这决定了是否可以在运行时通过全局着色器关键字来重写。
默认是全局,但如果指定了局部,那就无非通过全局的方式来修改。
具体是添加“_local”
阶段
有以下几个阶段
- _vertex
- _fragment
- _hull
- _domain
- _geometry
- _raytracing
可以指定变体生成的阶段,顶点还是片元。
虽然unity会自动判别,假如如果一个变体仅在片元中生效,那会自动删除重复的顶点着色器,但这会浪费编译时间,所以最好顺手设定一下。
用关键字来作为条件
假使有如下代码
如果这里改用dynamic_branch,那unity其实是为每个关键字创建了一个布尔变量,要求GPU去动态的判断。
而如果是shader_feature or multi_compile ,则会依据当前的关键字状态,将对应的变体发送到GPU。
其他语句
除了if,还可以使用如下的hlsl语句
- #if, #elif, #else and #endif.
- #ifdef and #ifndef.
使用这些的话会导致以后更改#pragma更加困难,如更难将multi_compile 更改为 shader_feature。(存疑,没有体会到)
为禁用关键字创建变体
如果使用shader feature创建单关键字,那在禁用后还是会自动创建第二个启用的变体。即类似默认添加_。
而如果使用multi_compile创建单关键字,不声明则不会自动创建。
但如果是在使用multi_compile或者shader_feature创建两个或以上的关键字组,则可以在之前声明”_”,即为禁用全部关键字时创建一个变体。
shader_feature 如没有添加”_”,默认取第一个关键字,添加后则默认为空。 手动在mat中启用其他非默认的关键字,之前的默认关键字还是会生成变体。
这里规则相当的绕,牵涉各种特殊情况,又和shader中的skip unused shader_feature有关,建议还是通过查看shader文件的变体文件来确认最终有多少和那些变体。
快捷方式
untiy定义了一些快捷指令,以方便开关某些具体的功能,如点光源阴影,雾气,GPU实例化等等。
配合材质窗口
可以通过在Properties中使用Toggle、ToggleOff或者KeywordEnum等来方便的控制。