unity

AssetBundle中的shader变体丢失问题

2023-10-25  本文已影响0人  vectorZ

我们通过assetbundle加载材质球或shader时常遇到一个问题:在电脑上测试ok的shader,在手机上显示一片粉红。
出现这种情况的原因有很多,但是其中可能性最大的就是shader变体丢失导致的。

ab资源加载中的shader是如何被引用到的?

加载ab资源中的材质时,会主动索引并加载使用的shader。
“被包含在构建包体中”,指的这几种情况:

  1. shader文件在Resources文件夹下;
  2. shader文件被设置在Setting => Graphics => Always Included Shaders中;
  3. shader被构建包含的场景中使用;这几种情况下shader资源会被打进apk包中。

基于shader是否在构建包体中,有以下2种情况:

为什么ab中的shader变体会丢失?

multi_compile和shader_feature是着色器中定义宏的关键字,不同的关键字会生成不同的变体。
相比于multi_compile指令,使用 shader_feature 指令定义的着色器在构建时,没有在材质球中使用到的变体将不会包含在最终的构建中。
所以 shader_feature 用于材质中设置的关键字,而 multi_compile 更适合通过代码来全局设置的关键字。

在Assetbundle打包材质与其引用的shader时:

如何避免shader变体丢失?

第一种方法:将shader加入到ProjectSetting=>Graphics=>Always Included Shaders 中。
加在这里的shader不会剔除变体,所以适用于变体少且全部使用到的shader。
如果加入有很多变体的shader,会导致打包包体膨胀。
注意:shader_feature定义的关键字如果未使用,即使加入到了这里,变体也不会保留!

第二种方法:使用ShaderVirantCollection。
我们可以使用ShaderVirantCollection文件来定义使用到的变体,这样打ab包时会自动将定义的变体编译并打包,这样就避免了变体丢失。
注意:ShaderVirantCollection也必须入包(与shader放入同一个ab包),否则不生效。

可以通过shader control插件来搜集使用到的shader变体,从而避免人工查找容易漏掉了某些变体。
当然shader control可以仅可以查询到项目中材质球使用到的shader关键字,如果是通过代码加载/设置shader keyword,则无法通过shader control查询到。

assetbundle打包shader的最佳方案

下面是我当前项目中assetbundle打包shader的方案,暂时定义为我自己的最佳方案:

multip_compile 和shader_feature的区别

相同点

#pragma multi_compile A B
//OR #pragma shader_feature A B

不同点
如果使用shader_feature,build时没有用到的变体会被删除,不会打出来。也就是说,在build以后环境里,运行代码Material.EnableKeyword("B")可能不起作用,因为没有Material在使用变体B,所以变体B没有被build出来,运行时也找不到变体B。
如果想解决这个问题,可以采取以下办法中的其中一种:

  1. 使用multi_complie 代替 shader_feature,multi_complie 会把所有变体build出来;
  2. 把这个Shader加入“always included shaders”中 (Project Settings -> Graphic);
  3. 创造一个使用变体B的Material,强行说明变体B有用;

局部keword

全局的Keyword只能有256个,这或许会最终对我们造成限制,而且大部分Keyword并不需要进行全局声明。
因此,我们可以使用multi_complie_local来声明局部的、只在该Shader内部起作用的Keyword,用法相似:

#pragma multi_compile_local __ A
//OR #pragma shader_feature_local __ A

注意:

参考链接

Unity Manual - 着色器变体和关键字
Unity Blog - Stripping scriptable shader variants
【Unity游戏开发】马三的游戏性能优化自留地

上一篇下一篇

猜你喜欢

热点阅读