【Unity3D】Shader变体管理流程-变体剔除

一、什么是Shader变体管理

  想要回答这个问题,要看看什么是Shader变体。

1. 变体

  我们用ShaderLab编写Unity中的Shader,当我们需要让Shader同时满足多个需求,例如说,这个是否支持阴影,此时就需要加keyword(关键字),例如在代码中#pragma multi_compile SHADOW_ON SHADOW_OFF,对逻辑上有差异的地方用#ifdef SHADOW_ON#if defined(SHADOW_ON)区分(#if defined()的好处是可以有多个条件,用与、或逻辑运算连接起来):

Light mainLight = GetMainLight();
float shadowAtten = 1;
#ifdef SHADOW_ON
    shadowAtten = CalculateShadow(shadowCoord);
#endif
float3 color = albedo * max(0, dot(mainLight.direction, normalWS)) * shadowAtten;

 

然后对需要的材质进行material.EnableKeyword("SHADOW_ON")material.DisableKeyword("SHADOW_ON")开关关键字,或者用Shader.EnableKeyword("SHADOW_ON")对全场景包含这一keyword的物体进行设置
  上述情况是开关的设置,还有设置配置的情况,例如说我希望高配光照计算用PBR基于物理的光照计算方式,而低配用Blinn-Phong,其他计算例如阴影、雾效完全一致,也可以将光照计算用变体的方式分隔。

如果是shader编写的新手,可能有两个问题:
①我不能直接传递个变量到shader里,用if实时判断吗?
答:不可以,简单来说,由于gpu程序需要高度并行,shader中的分支判断需要将if else两个分支都走一遍,假如你的两个需求都有不短的代码,这样的开销太大且不合理。
②我不可以直接将shader复制一份出来改吗?
答:不是很好,例如你现在复制一份shader出来,还需要对应脚本去找到需要替换的shader然后替换。更重要的是,当你的shader同时包含很多需要切换的效果:阴影、雾效、光照计算、附加光源、溶解、反射等等,总不能有一个需求就shader*2是吧

  当你有多组关键字,阴影是否开关,是否有雾效时,你可能会写出下面这样的关键字声明:

#pragma multi_compile SHADOW_OFF SHADOW_ON
#pragma multi_compile FOG_OFF FOG_ON
#pragma multi_compile ADDLIGHT_OFF ADDLIGHT_ON
#pragma multi_compile REFLECT_OFF REFLECT_ON
//something keyword ...

 

这种写法属于比较死亡的写法,别在意,后面自然会说出各种写法中不好的地方并提出回避建议。

  而对于当前材质,就会利用上述的关键字进行排列组合,例如一个“不希望接受阴影,希望有雾,需要附加光源,不带反射”,得到的Keyword组合就是:SHADOW_OFF FOG_ON ADDLIGHT_ON REFLECT_OFF,这个Keyword组合就是一个变体。对于上面这个例子,可以得到2的4次方16个变体。

 我们知道了什么是变体,再来回答为什么要变体管理。
  可以发现上述例子中,每多一条都会乘2,实际上一列keyword声明可以不止两个,声明三个、甚至更多也是可能的。
  但不管怎么说,随着#pragma multi_compile的增加,变体数量会指数增长。这样会带来什么问题呢?
  这时候需要了解下shader到底是什么。

2. Shader

  我就当大家都知道,ShaderLab其实不是很底层的东西,它封装了图形API的Shader,以及一堆渲染命令。对于图形API,Shader是gpu的程序,不同API上传shader略有区别,例如OpenGL:

GLuint vertex_shader;
GLchar * vertex_shader_source[];//glsl源码
//创建并将源码传递给GPU
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 
### Unity 中如何使用 Shader Variant Collection 优化 Shader 变体管理 Shader 变体Shader Variants)是 Unity 根据不同材质属性、光照模型、渲染队列等生成的不同 Shader 版本。虽然这些变体提供了灵活的渲染选项,但过多的变体会显著增加构建体积和加载时间。为了解决这一问题,Unity 提供了 **Shader Variant Collection** 工具,用于集中管理和优化变体的使用[^2]。 #### 创建与配置 Shader Variant Collection 在 Unity 编辑器中,可以通过菜单 `Create > Shader > Shader Variant Collection` 创建一个 Shader Variant Collection 文件。该文件可以包含多个 Shader变体组合,并支持手动添加或自动收集变体[^3]。 ```csharp // 示例:通过脚本加载 Shader Variant Collection 并进行预加载 ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>("Assets/MyShaderVariants.shadervariants"); svc.WarmUp(); ``` #### 变体剔除与优化 Shader Variant Collection 支持在构建时进行变体剔除Shader Variant Stripping),即根据设定的规则剔除未使用的变体。这可以通过设置特定的关键字组合来控制哪些变体被保留,哪些被移除。这样可以减少最终构建的包体积和运行时的内存占用[^1]。 此外,Unity 2022.3 开始支持“严格的着色器变体匹配(Strict Shader Variant Matching)”选项,确保 Unity 只尝试加载与所需本地和全局关键字组合完全匹配的变体,从而进一步优化变体加载效率[^4]。 #### 预编译与运行时 Warmup Shader Variant Collection 还支持预编译机制。通过调用 `WarmUp()` 方法,可以在运行时或启动时预加载所有指定的变体,避免在实际渲染时因变体未编译而导致性能抖动。这种方式特别适用于 Addressable 资源加载流程中,可以显著减少重复编译的开销。 #### 与 Addressable 资源系统的集成 在 Addressable Asset System 中,将 Shader Variant Collection 作为独立资源打包,并在构建时加载这些集合,可以有效控制 Shader 变体的编译时机和频率。通过资源依赖分析,仅在 Shader 或其依赖项发生变化时才重新构建相关资源包,从而避免不必要的 Shader 编译[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凉亭下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值