Unity Shader - Making multiple shader program variants 制作shader程序多变体

目录:Unity Shader - 知识点目录(先占位,后续持续更新)
原文:Making multiple shader program variants
版本:2019.1

Making multiple shader program variants

制作shader程序多变体

通常它将方便保留大部分shader代码是不变得,但允许少量不同的shader “variants” 将会被生成。这通常叫"超集shader"( “mega shaders” or “uber shaders” ),变体是根据shader代码中的预处理指令的每种情况,分别编译多次,编译出不同版本的变体。

为了使用这点,你可以在shader snippet(shader代码段) 使用 #pragma multi_compile 或是 #pragma shader_feature 指令。这同样可作用于 surface shader

在运行时,Unity将根据 材质关键字(Material.EnableKeywordMaterial.DisableKeyword)或是全局的 shader 关键字(Shader.EnableKeywordShader.DisableKeyword)来选用shader变体版本。

How multi_compile works

multi_compile 是怎么工作的

指令例子:

#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON

这个例子的指令将生成2个shader变体:一个是 FANCY_STUFF_OFF 定义的,另一个是 FANCY_STUFF_ON。在运行时,Unity可在Material或是全局的shader关键字中激活它们的其一。如果两个关键字都启用了,那么将会只用第一个(如上面例子中,只会使用 FANCY_STUFF_OFF)。

你可以在同一行的 multi_compile 添加更多的关键字。如:

#pragma multi_compile SIMPLE_SHADING BETTER_SHADING GOOD_SHADING BEST_SHADING

这个例子的指令将生成4个shader变体: SIMPLE_SHADINGBETTER_SHADINGGOOD_SHADINGBEST_SHADING

生成一个无变体宏定义的shader,添加一个名为下滑线即可(__)。这是中很常用的技术,用来避免使用两个关键字来做开关,因为在一个项目中,关键字的数量是有限的(查看后面的小节 Keyword limits)。例如:

#pragma multi_compile __ FOO_ON

这个指令生成的shader变体有:一个啥也没定义的(__),和另一个使用 FOO_ON 定义的。

Difference between shader_feature and multi_compile

shader_feature与multi_compile的区别

shader_featuremulti_compile 非常类似。它们唯一的区别是:使用 shader_feature 的话,在unity构建发布时,是不会包含上未启用的 shader_feature 的变体代码。就为这个原因,你就应该使用 shader_feature 来给你的材质定义关键字,而 multi_compile 对应从代码中来设置全局关键字的方式比较适合。

另外,还有一种简单的写法,只有一个关键字:

#pragma shader_feature FANCY_STUFF

其实是 #pragma shader_feature _ FANCY_STUFF 的简写。最终编译后也会是生成两个shader变体(第一个是无定义的,第二则有。)

Combining several multi_compile liens

如果你提供的 multi_compile 有多行定义,Unity最终编译将尽可能的将这些行分别组合。例如:

#pragma multi_compile A B C
#pragma multi_compile D E

第一行生成3个变体,而第二行是2个。但总的变体数量为6个(A+D,B+D,C+D,A+E,B+E,CE)。
(假设有3行,每行有:A,B,C个,总量就是:ABC个,所以multi_compile 定义行,且每个都用在代码定义判断,那么该shader编译出来的变体就会非常的多,导致shader资源变得很大。)

你可以将每一行的 multi_compile 看作是控制shader的一个"feature"(功能特性)。要记住,这种方式会使shader变体数量极快速的增长。例如,10行 multi_compile 特性定义,每行2个,那么最后将会生成 1024 个shader变体。

Keyword limits

关键字的限制

在Unity中的shader变体关键字的数量限制最多为256个(全局关键字256个,注意是全局的,因为下面还会介绍局部的),而Unity内部本身使用了60个(因此用户能使用量就是(256-60)个)。关键字在Unity项目中是全局启用的,所以当你在几个不同的着色器中定义多个关键字时,请注意不要超过这个限制。

Local keywords

局部关键字

shader_featuremulti_compile 关键字的缺点是:定义后都会占用Unity全局关键字数量(256个全局关键字,加上64个局部关键字)。为了避免这个问题,你可以使用不同的shader变体指令:shader_feature_localmulti_compile_local

  • shader_feature_local:类似于 shader_feature,但每个关键字都是局部的。
  • multi_compile_local:类似于 multi_compile,但每个关键字都是局部的。

局部指令将保留定义的关键字都在定义它们的shader里,而不是引用于整个项目的。就为了这个原因,你就应该使用局部的关键字来替代全局关键字,除非你需要通常全局的脚本API来控制指定的关键字的启用/禁用。

当你使用关键字时,你可能会看到对性能的影响,但这都取决于你的项目是如何使用关键字的。每个shader的局部和全局关键字的总数都会影响性能:尽量以这种方式处理,尽量使用局部关键字,而少些使用全局关键字,每个shader都尽可能的减少关键字的总数量。

如果全局关键字与局部关键字同名,Unity键优先使用局部关键字。

Limitations

限制

  • 不通能通过控制全局关键字开关的API来控制shader中的局部关键字(如:Shader.EnableKeyword或是CommandBuffer.EnableShaderKeyword)。
  • 每个shader的局部关键字最大的数量是64个。
  • 如果启用了一个shader没有定义的局部关键字,那么Unity将会去创建一个新的全局关键字。(当你是用全局的将会生成全局的,当你使用局部的API将生成局部的,如,Shader.EnableKeyword将生成全局,Material.EnableKeyword将生成局部的

Exmaple

例如

#pragma multi_compile_local __ FOO_ON

该指令将生成2个shader变体:一个无定义的(__),和另一个 FOO_ON 的局部关键字。

启用方式与启用全局关键字方式一样:

public Material mat;
Private void Start()
{
    mat.EnableKeyword("FOO_ON");
}

Built-in multi_compile shortcuts

内置的 multi_compile 的关键字快捷方式

编译shader多变体也有那么一些快捷的符号。它们大多数用于处理不同的光照,阴影和静态烘焙图。查看文档 rendering pipeline 了解详情。

  • multi_compile_fwbase 编译处理PassType.ForwardBase 所需要的。这些变体分别处理不同类型的静态光照烘焙图,和主要方向光阴影的启用或禁用。
  • multi_compile_fwdadd 编译处理 PassType.ForwardAdd 所需要的。这些变体分别处理方向光,聚灯光,或点光源类型和他们不同的烘焙贴图。
  • multi_compile_fwdadd_fullshadowsmulti_compile_fwdadd 一样,但也包含了实时光影的处理。
  • multi_compile_fog 处理了一些不同雾化类型处理的变体(雾化算法模型:off/linear/exp/exp2)

多数内置的关键字快捷方式都会生出许多的shader变体。如果你的项目不需要其他的一些功能,你可以使用 #pragma skip_variatns 来跳过一个变体的编译。例如:

#pragma multi_compile_fwdadd
#pragma skip_variants POINT POINT_COOKIE

这个指令跳过了所有包含 POINT 或是 POINT_COOKIE 的变体编译。

Shader hardware variatns

shader硬件变体

shader的硬件变体允许给你提供对不同的硬件兼容做特殊的优化。你可以简单的创建能高效运行在指定平台的高度和低端的硬件的shader(如:OpenGL ES)。

为了启用shader硬件变体的生成,添加 #pragma hardware_tier_variatns rendererrenderer 是有效的 shader program pragmas 渲染平台的其一。使用这个 #pragma 定义下面3个shader变体,不论其他的关键字怎么样。每个变体都会有下面定义的变体之一来组合:

UNITY_HARDWARE_TIER1
UNITY_HARDWARE_TIER2
UNITY_HARDWARE_TIER3

(2019.1.7f1新版本中的UnityEngine.Graphics.activeTier 枚举对应上面的变体定义)

使用这些变体来为低端或高端硬件编写能保底运行或是额外功能的shader。在Unity编辑中,你可以使用图形仿真菜单(旧版本在:Editor->Grapphics Emulation,新版本(2019)在:Editor->Graphics Tiers,可以叫:图像级别)来测试任意的硬件级别,你可以在那里改变每个硬件级别。

为了让变体尽可能的小,Unity仅会发布运行后只加载一种。相同的shader(例如,如果你仅为TIER1级别的写一些版本,且其他的都一样)它们不会占用额外的硬盘空间。

在加载时,Unity会去检测GPU而自动选择对应的TIER级别。如果不能自动检测出GPU的硬件标准则默认使用最高级别的TIER值。你可以设置 Shader.globalShaderHardwareTier (Shader.globalShaderHardwareTier是旧版本的,新版本现在使用的是 UnityEngine.Graphics.activeTier)来重置TIER值,但你必须在Unity加载shader变体前设置。一个比较好的位置是,将设置 TIER 的脚本放到预加载场景,该预加载场景是加载主场景前就加载的,只要确保这个预加载场景有没任何shader加载就好了,这样可以减少BUG,可更好的维护。

这些 shader 硬件层级(TIER)与Unity的Quality Settings没有关系。它们是从Unity运行时就对GPU检测的层级设置。

Platform shader settings

shader平台设置

注意注意:(可以不看,因为现在都2019.1版本的文档了,但官方对于这个Platform shader settings的说明不是一般的混乱,在unity的论坛也是很多人吐槽不是一般的乱,所以这里翻译Platform shader settings,就不要看了,因为实在写得乱七八糟,而且很多旧的文档也没及时更新。)

除了为不同的硬件级别调整着色器代码,您可能还想调整Unity的内部#define(例如,您可能想在移动设备上强制启用阴影映射)。了解如何实现这点的详情,查看 UnityEditor.Rendering.PlatformShaderSettings(这个是旧版的,现在2019.1新版的是UnityEditor.Rendering.TierSettings),提供覆盖每个级别当前支持功能特性项。

使用UnityEditor.Rendering.EditorGraphicsSettings.SetShaderSettingsForPlatform(这个是旧版本的,2019.1新版是EditorGraphicsSettings.SetTierSettings,然后发现TierSettings还是这个EditorGraphicsSetting都是 UnityEditor命名空间下的,也就说,和上面发布后运行没关系的,也就说,你如果想实现,游戏中控制底,中,高端配置不一样的shader设置,这个Platform shader settings没有半毛钱关系)调整每个平台,每个级别的shader平台的设置。

如果使用了 UnityEditor.Rendering.PlatformShaderSettings 来设置不同级别的不一样的设置,那么Unity将会生出shader对应级别的变体,就算没有定义 #pragma harware_tierr_variants

(如果要实现游戏中画面设置:底,中,该之类的,还是自己用multi_compile来实现shader的质量选择,当然有些是脚本所以的,也是需要自己来控制的)

另外可以看看Unity官方上其他用户回复中的吐槽:

See also

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值