<Shader模块化>0.ShaderVariant和multi_compile编译Shader的多个版本

 
一ShaderVarian的使用
1. 首先在CGPROGRAM 下 申明 需要多版本编译的 几个版本的key,Material同一时刻只能对应它所使用的Shader的一个variant。
            #pragma multi_compile A B
如果C#代码没指定,Unity会默认使用 A这个编译版本。
           当B打开,Unity使用 B这个编译版本。 当B关闭,即使我们没有在C#中显式的打开A,也会使用A版本。
2. 在CGPROGRAM里的cg代码中 ,控制逻辑,
  不能用在外面,并且不能使用 elseif
#ifdef A
XXXX
#endif
 
#ifdef B
XXXX
#endif
 
 
3.在C#代码里使用Material.EnableKeyword("A")和Material.DisableKeyword("A")来开关对应的宏
  Material.EnableKeyword(keyword: string)/DisableKeyword(keyword: string)  //是全局设置
     someMaterial.EnableKeyword(keyword: string)/DisableKeyword(keyword: string)  //也可以对某个材质单独设置
 
  Shader.EnableKeyword(keyword: string)/DisableKeyword(keyword: string)//是全局设置
  XXXXShader.EnableKeyword(keyword: string)/DisableKeyword(keyword: string)//也可以对XXXXShader单独设置
 
二,注意事项
 multi_compile和shader_feature的区别
1.如果你在shader中添加了
1 #pragma multi_compile  _A _B
2 #pragma multi_compile  _C _D
  那么无论这些宏是否真的被用到,你的shader都会被Unity编译成四个variant,分别包含了_A _C,_A _D, _B _C,_B _D四种keyword组合的代码
2.如果是
1 #pragma shader_feature _A _B
2 #pragma shader_feature _C _D
  那么你的shader只会保留生成被用到的keyword组合的variant,至于如何判定哪些组合被用到了,等后面提到Assetbundle时候再说。
 
 
4. 不要频繁用此命令,因为关键字的个数是有上限的,而多条multi_complie会交叉匹配产生很多种结果。
 
5. 在属性列表 可以给出选项
    Properties {
 
[KeywordEnum(None, Ice)] _Overlay ("Overlay mode", Float) = 0
}
 
 
三,一些BUG。。。
但是这里有个BUG,当你在编辑器面板手动选择一个KEY后,mat的key也只有一个了, 在PC上好用,打包Assetbundle后也只有这个版本。
 

 

6.尝试用NotePad++打开.mat文件(需要在编辑器设置里把AssetSerializationMode设置为Force Text):
如果多版本编译成功,会看到红圈里的几个版本
 
如果

 

不成功,或者没有多版本,则 后面为空
7. 注意事项:
   在编辑器上, 即使打开mat上的m_ShaderKeywords为空,多版本编译也是没问题的, 因为是动态编译。在代码里切换表现正常。
   当打包AssetBundle到手机或者PC版本,则无法切换了。
    如果你手头有built-in Shader的源码可以打开里面的StandardShaderGUI.cs看一下Unity自己事怎么处理对于StandardShader的keyword设置的。
  
   所以,如果出现m_ShaderKeywords为空,则把材质切换为另一个材质,先保存,再切换回来,在保存
 
 
 

 

四:解决方案
解决方法1:把Shader放到在ProjectSetting->Graphics->Always Include Shaders列表里,
把该shader加入项目设置,
 

 

同事在项目的设置里默认为 strip Unused,打包会跳过Unused的变量。 设置为keep,则可以打包出所有shader。
 
解决方法: 使用ShaderVariantCollection
         ShaderVariantCollection可以让我指定某个shader要编译都要编译带有哪些keyword的变种。
        并且在ProjectSetting->Graphics界面新加了一个Preloaded Shaders列表。
 

 

如果不放这里,也可以使用代码加载
 
ShaderVariantCollection shaderVariantCollection = Resources.Load <ShaderVariantCollection>( "MainShaderVariant");
if (shaderVariantCollection )
      shaderVariantCollection.WarmUp ();
 
这种好处:不会像Always Include Shaders列表中的那样全部变种都生成。
 
 
 
 
      放到AB包就又涉及到shader_feature的处理,为了在运行时动态切换材质的shadervariant,可以在工程里新建一堆材质,然后把每个材质设置成一种想要的keyword组合,把他们和shader放到一起打到一个AB中去,这样虽然能让shadervariant正确生成,但是这些Material是完全多余的。
  为了解决这种问题,Unity5.0以后引入了ShaderVariantCollection(下面简称SVC),这里不讲用法,只说问题,这个SVC文件可以让我指定某个shader要编译都要编译带有哪些keyword的变种。并且在ProjectSetting->Graphics界面新加了一个Preloaded Shaders列表,可以让你把SVC文件放进去,编译时指定的Shader就会按照SVC中的设置进行正确的variant生成,而不会像Always Include Shaders列表中的那样全部变种都生成。
  但是它在AB中的表现可就不尽如人意了,要让它起作用,就必须把它和对应的shader放在一个AB中,而且除了5.6以外版本,我试了几个都不能正确使用,不是一个variant都没生成,就是只生成一个shadervariant(和放一个没有设置keyword的材质效果一样).你可以自己用UnityStudio打开查看一下生成的AB内容。
 
 
解决方法3:把Shader放到Resources文件夹下,也会正确处理,我猜也应该是全部keyword组合都编译,有知道的同学,麻烦留言告诉我。
 
 
解决方法4:用工具生成与该shader对应的 ShaderVariantCollection,与这个shader打到同一个bundle里。
 
 
解决方法5: 放到AB包就又涉及到shader_feature的处理,为了在运行时动态切换材质的shadervariant,可以在工程里新建一堆材质,然后把每个材质设置成一种想要的keyword组合,把他们和shader放到一起打到一个AB中去,这样虽然能让shadervariant正确生成,但是这些Material是完全多余的。
 
 
四、创建ShaderVariantCollection
       4.1、全手工创建
       这是最笨的一种方法,需要自己在Project视图下创建ShaderVariantCollection资源,然后使用上面介绍的两个窗口自己添加着色器变体
 
 
       4.2、使用Unity收集当前使用到的着色器变体
       为了最快速、最方便的帮助我们创建和收集那些真正被用到的着色器和他们的变体,Unity已经在编辑器中内置了可以追踪那些已经被使用到的着色器和他们的变体,并可以直接生成相应的ShaderVariantCollection资源。
       打开图形设置面板(Edit->Project Settings->Graphics)
 
 
 
 
       这时候只需要依次打开我们的所有场景,把需要的物体都显示一遍,Unity就会自动记录下来哪些着色器的哪些着色器变体已经被使用到。统计完后只需点击下面的保存按钮就可以生成我们所需要的ShaderVariantCollection资源。当然你也可以为你的每一个场景或者按需生成足够多的ShaderVariantCollection资源。
 
 
 

 ShaderVariant与Assetbundle的关系

  我所遇到的问题正是和Assetbundle有关,原因是打成AB包之后shader_feature所定义的宏没有被正确包含进去。
  上面说了multi_compile定义的keyword是一定能正确的生成对应的多种组合的shaderVariant,但shader_feature不尽然,Unity引入shader_feature就是为了避免multi_compile那种完整编译所导致组合爆炸,很多根本不会被使用的shader_variant也会被生成。Unity在处理shader_feature时会判断相应的keyword组合是否被使用
  1.如果shader没有与使用它的材质打在一个AB中,那么shader_feature的所有宏相关的代码都不会被包含进AB包中(有一种例外,就是当shader_feature _A这种形式的时候是可以的),这个shader最终被程序从AB包中拿出来使用也会是错误的(粉红色).
  2.把shader和使用它的材质放到一个AB包中,但是材质中没有保存任何的keyword信息(你在编辑器中也是这种情况),shader_feature会默认的把第一个keyword也就是上面的_A和_C(即每个shader_feature的第一个)作为你的选择。而不会把_A _D,_B _C,_B _D这三种组合的代码编译到AB包中。
  3.把shader和使用它的材质放到一个AB包中,并且材质保存了keyword信息(_A _C)为例,那么这个AB包就只包含_A _C的shaderVariant.
  可以看到shader_feature所定义的keyword产生的ShaderVariant并不是全部被打包到AB中,特别是你想在游戏运行时动态的通过EnableKeyWorld函数来进行动态修改材质使用的shaderVariant,如果一开始就没有把对于variant放进AB包,自然也就找不到。

posted on 2018-08-28 00:17  百无禁忌  阅读(483)  评论(0编辑  收藏  举报

导航