Unity3d 开发 通过 shader编译参数 整合代码


在编写大型项目的shaders时,大多数像素混合算法都是通用的,而且基本上一旦完成就不会更改。另一方面,在shader中不宜做分支逻辑,会引起效率上的消耗。因此很为了更好的组织shader代码,Unity引入了 shader编译参数 来解决这个问题。

原理

由于GPU中的寄存器结构与CPU不同,在显卡中运行的shader没有类的概念,也就没法套用各种设计模式。为了更好的组织shader代码,采用退而求其次的方式,保证大部分代码不动,而允许shader有不同的变体(variants)存在。这通常被称为超级shader(UberShader)。可以理解为写出一个万能的模板然后向下做减法,这样就不需要每次都写代码。当需要拆分逻辑时,就使用 shader编译参数 ,从编译层削减逻辑,最大限度的减少分支判断。

另一方面,使用类似C++的文件包含机制,可以将公用的方法提取分层,便于文件结构的调整。

编译参数

说了半天编译参数,在Unity中只有两个,它们分别为:

  • #pragma multi_compile
  • #pragma shader_feature

实例

为了清楚的说明这些参数的用法,我写了下面这个例子:

用法讲解

总的说来, shader编译参数有三种用法,即静态参数,半动态参数,动态参数,下面分别说明。

静态

静态参数是指宏定义。

这种参数的使用方法很像C++中的编译宏。通过控制#define xxx 1/0来开关功能。它的特点是纯静态的,即它会完全的编译到文件中。适合多个文件#include的情况。

半动态

使用shader_feature参数可实现shader半动态编译。具体说来,只有用到的属性才会作为变体(variants)写到编译参数中,否则就相当于不存在。换句话说,如果一个shader中有很多使用频率低的功能,不希望它们编译游戏时带入内存,就应选择这个模式。使用时,编写如下代码:

即可在材质面板中通过勾选改变 shader编译参数 了。

下面举例说明它的特性。使用上面的shader创建一个材质球。勾选Fan选项。

然后选择shader文件>点击Compile and show code按钮右侧的三角形>下拉菜单中选择Show.

点击后会打开一个记录着变体数量的文件,内容如下:

接着我们再创建一个材质,勾选SongYang,如下图所示

再打开变体说明文件时,内容已经自动发生了变化:

这就是半动态编译。由于这种 shader编译参数 会根据使用情况变化,可以极大的减少实际shader的变体数量,在需要在材质面板直接设置时,它是首选方案。

动态

对于不知道是否会动态开启宏的属性,当需要代码控制 shader编译参数 开关时,需要使用纯动态的编译参数。这意味着每一条参数要与其他参数做组合计算。当有多条动态参数时,shader的数量会急速上升,导致内存占用增大,因此需要谨慎使用这项技术。它的使用方式为:

在Unity中的关键字数量有限制,它最大值为128(版本为Unity5.x),由于动态编译参数会使变体快速增长,使用时要小心控制数量。另外,切换动态编译参数实际上重新创建了材质,过度的运行时切换可能会造成性能问题。

默认值

下面两个代码基本等价:

唯一的不同是,它们的默认值不同。如果不希望第一个编译参数默认打开,就需要加入__。这种用法常见于半动态编译,因为很多情况下半动态编译都是开关型选择,而使用它可以避免占用变体数量。

短路逻辑

需要注意一点,不论是shader_feature还是multi_compile,它们都是短路逻辑判断。即一旦满足条件,后面的编译参数不会带上。

当使用半动态编译参数时,例如我将三个选项全选中:

编译后的结果是这样的:

Song这个编译参数并没有生效。

当使用动态编译参数时,也是按顺序打开宏。例如我将R_ONB_ON都打开,实际只有R_ON相关代码生效。当关闭R_ON之后,B_ON才会生效。

忽略参数

有时需要忽略某些参数来消减变体数量。可以使用skip_variants,例如下面的写法:

消减内置变体数量

在Unity提供的shader中有很多默认的变体值,但通常我们不需要这么多。比如,场景中不需要实时的方向高光(Realtime Directional Specular),就可以将对应的编译宏去掉;比如场景中不需要指数雾效,也可以将其去掉。

设置方式是,点击Edit菜单>Project Setting>Graphics。打开后在Inspector中找到shader stripping,然后在下拉菜单中选择Manual,对其进行内容相关的调整。如下图所示:

具体这些参数的含义可以参考官方文档

总结

本文主要介绍了如何使用三种 shader编译参数 整合代码。每种参数都有自己的优缺点。 最后提醒一下,当心shader中的FallBack,如果使用不当,可能会引入大量出乎意料的变体。

本文出自 松阳论道 转载必须注明出处

http://blog.songyang.net/379.html

说点什么吧...

电子邮件地址不会被公开。 必填项已用*标注