admin管理员组

文章数量:1650774

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/Manual/SL-MultipleProgramVariants.html

 

本文标签: 变体ShaderUnityshaderfeaturemulticompile