unity shader变体之#pragma multi_compile 和 #pragma shader_feature

10 篇文章 3 订阅
3 篇文章 2 订阅

shader为什么需要变体:

        写shader的人,都知道要减少if条件分支语句的使用,原因在于,当你的shader执行一条指令的时候,并不是操作一个像素,而是同时操作一组像素,并一条一条指令向下执行。如果if的两条分支都有可能执行,那么执行的逻辑就是两条分支的和,因此当if条件中仅有uniform变量和常量组成的时候,if的选择是固定的,if不会两个条件都走到,那么也就没有性能问题。但是实际问题并不是所有的条件语句都是确定性结果的,大多数我们执行的时候是需要判断动态的结果的,那么怎么办,unity提供了一种叫shader variants的概念,叫做shader变体,来解决这个问题。

在最新的URP中,在Lit着色器中大量的使用了这些变体:

shader variants介绍:

        shader变体,如字面意思,就是说,根据一个我们写的shader,衍生多个版本的shader,这里多个版本是通过我们定义的变体生成的,那么是怎么生成的呢?

1.首先我们在CGPROGRAM中先定义:

#pragma multi_compile _ PAINTSTYL_1 PAINTSTYL_2 PAINTSTYL_3 PAINTSTYL_4 PAINTSTYL_5 PAINTSTYL_6

2.然后我们在着色器程序中通过类似:

#if defined( PAINTSTYL_1 ) 
	color = lerp(color, _CustomColor1, Masks.r);
#endif

这样的语句,我们可以在只有定义PAINTSTYL_1 时,才会编译。

在编译的时候,shader会识别到#pragma multi_compile这个指令,然后通过下面的#if 预编译指令,把shader编译成 多个版本(multi_compile 中有多少种情况就有多少种)的变体,这样就不存在我们使用if语句时,出现的多分支都会执行的性能问题了。

那么我们怎么指定要执行哪个变体呢:

material.EnableKeyword("PAINTSTYL_1");
material.DisableKeyword("PAINTSTYL_1");
shader.EnsableKeyword("PAINTSTYL_1");
shader.DisableKeyword("PAINTSTYL_1");

material 与 shader 指定keyword的区别在于material是指定当前材质的变体,shader是指定所有使用这个shader的材质的变体。

 

#pragma multi_compile 与 #pragma shader_feature的区别:

        shader_feature与multi_compile非常相似。唯一的区别是Unity在最终的版本中不包括shader_feature着色器的未使用的变体。出于这个原因,你应该使用shader_feature来处理从material中设置的关键字,而multi_compile更好地处理从全局代码中设置的关键字。

 

Local keywords:

shader_feature和multi_compile的主要缺点是,定义的所有关键字都限制了Unity的全局关键字数量(256个全局关键字,加上64个本地关键字)。为了避免这个问题,我们可以使用不同的着色器变体指令:shader_feature_local和multi_compile_local。

  • shader_feature_local: 与 shader_feature类似, 但是仅限本shader使用
  • multi_compile_local: 与multi_compile类似, 但是限本shader使用

使用更多的Local keywords和更少的globalkeywords,以减少每个着色器的关键字总数,这样可以减少变体的编译数量。因为变体的编译时根据关键字的数量相乘得到的,比方说:

#pragma multi_compile A B C
#pragma multi_compile D E

那么就会生成 3 * 2 = 6种,

限制:

  • 不能在api中使用本地关键字来改变全局关键字(比如着色器)。EnableKeyword或CommandBuffer.EnableShaderKeyword)。
  • 每个着色器有一个最大的64个唯一的本地关键字。
  • 如果一个材质启用了一个local关键字,并且它的着色器没声明用哪一个,那么Unity会创建一个新的global关键字。
public Material mat;
Private void Start()
{
    mat.EnableKeyword("FOO_ON");
}

 

查看变体:

我的变体定义如下:

#pragma multi_compile_local _ PAINTSTYL_1 PAINTSTYL_2 PAINTSTYL_3 PAINTSTYL_4 PAINTSTYL_5 PAINTSTYL_6 PAINTSTYL_7 PAINTSTYL_8 PAINTSTYL_9

选中我们的shader,然后在Inspector中查看Compiled code 可以看到,下面显示80个变体,为什么多了10倍呢?

我们可以下点面板下的Show,查看代码,发现还有一些unity 自带的scene的变体(下图只是一部分),也会被包含,因此我们也不能任意增加变体的数量。会导致变体代码膨胀。

也可以直接在面板上查看,点开Keywords:

也可以看到,还会有一些全局的keyworld被加入

 

参考:https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html

 

  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值