在unity的资源中,shader是比较特殊的一类。主要有下面几个疑问html
1. Shader算是代码,而且须要编译。那么是否能够热更新?android
2. AB中加载进来的shader是否能够经过shader.Find(名称)来索引?服务器
3.在使用shader_feature关键字后,build时忽略的变种是否要在运行时编译?函数
4.预编译shader cache的存储位置究竟在哪里?测试
直接补充最终结论:ui
1. shader能够热更新。spa
2. 使用multi_compile生成Shader Variant时,材质能够直接热更新。3d
3. 使用shader_feature生成Shader Variant时,可使用ShaderVariantCollection来记录全部使用到的变种,实现材质热更新。(目前仍有bug,必须将shader、SVC和全部材质放在一个AB中)htm
4. 不要用Shader.Find找本身包里的shader,使用AssetBundle.LoadAsset<Shader>()blog
5. shadercache随材质存储,材质能够热更新。
针对以上问题我作了一系列测试,记录以下:
测试一:
准备一个shader ,一个材质,一个cube作的prefab,各自打成一个AB。
在一个空场景中用脚本按以下顺序加载:
shaderAB->materialAB->prefabAB->prefab->GameObject。显示正常。
(事实上只要保证prefabAB->prefab->GameObject的顺序,materialAB和shaderAB在同一函数的任意位置均可以。Unity应该是延迟处理了资源的引用关系)
修改shader,打成新的ab,更名或者另存为备用。
发布之后,在文件夹中找到对应的shaderAB,使用新的shaderAB替换。
从新启动,效果已经更新。
结论一: shader能够热更新!
测试平台:android和pc standalone
代码稍做修改能够在运行时实现热更新。
测试二:
准备3个shader,引用同一个头文件。shader和cginc所有进入一个ab里。
运行时先加载shaderAB,而后用一个按钮切换shader
结果以下表:
|
热更新前 |
热更新后 |
Shader.Find |
正常(原shader) |
错误(shader丢失)[1] |
AssetBundle.LoadAsset[2] |
正常(原shader) |
正常(新shader) |
[1] 在Standalone或者移动平台上会有shader丢失;在Editor模式下会使用旧的shader,仔细分析后猜想是在Editor模式下,shader.Find的查找顺序以下:已加载的 AssetBundle->Shader源文件。而在发布平台上,因为没有散的shader源文件,因此直接丢失。
[2] AssetBundle.LoadAsset的路径要使用Manifest中记载的路径,以下形式:
Assets/Shaders/Src/shaderTest2.shader
结论二:能够在运行时手动替换上AB中的shader,但必须使用AssetBundle.LoadAsset!
·可使用cginc头文件!
·可使用文件夹管理Shader!
·最好彻底不使用Shader.Find,除非你100%肯定这个shader不会热更新。
关于Shader.Find,我的猜想以下:
Unity内部使用一个字典或者HashSet来支持Shader.Find,这里暂且叫它ShaderMap。ShaderMap的键是ShaderLab语法中的名字;值是Shader文件的GUID。
ShaderMap生成于Build项目时,保存了来自三个地方的shader cache引用关系:
1. Resources中的shader或Resources其中其余资源引用到的shader
2. 任意场景中引用到的shader
3. StreamingAssets中Asset Bundle内的Shader
运行时使用ShaderFind,只能找到这些Shader,若是对应GUID的shader不存在,查找就会失败,即便热更新后加入了新的Asset Bundle中含有同名Shader(即ShaderLab语法同名)。
4. 目前没有办法在发布之后动态更新ShaderMap。
测试三:
准备两个一样的shader,设定好#ifdef FEATURE,其一使用multi_compile,其二使用shader_feature
准备四个材质,分别对应
·multi_compile FEATURE on
·multi_compile FEATURE off
·shader_feature FEATURE on
·shader_feature FEATURE off
全部shader打成一个ab, 全部material打成一个ab
在运行时切换4个材质。结果以下:
·multi_compile FEATURE off |
正常 |
·multi_compile FEATURE on |
正常 |
·shader_feature FEATURE off |
正常 |
·shader_feature FEATURE on |
错误(和shader_feature FEATURE off同样) |
结论三:
·使用shader_feature的uber shader没法热更新!(结论已更新)
·若将shader存储于自定义AB时,仅按照全部shader_feature都没有定义的方式来编译。而且不会汇报这个编译过程当中的任何错误!(如:在shader中定义了shader_feature A B;而且依赖于A、B两者任一必须定义的话,编译就会出错。)
·Unity并不会在发布平台上编译缺失的变种。(直接拿个现有的变种凑数?)
测试四
放弃热更新shader,检查在使用shader_feature的时候,材质可否热更新。即可否在热更新时生成缺失的变种。
准备一个uber shader。再来3个材质,各使用不一样的变种,并分别打成m1,m2,m3三个包。发布时仅选择m1发布,而后在运行时热更新,使用m2,m3替换m1,显示效果达到预期。
这时候注意到m1,m2,m3体积分别为11,9,11KB,应该不仅是存有shader引用和相关参数。所以再将m1,m2,m3打为一个ab,体积为11kb。
结论四:
·在shader进入mainAssets的前提下,材质能够热更新。
·shader cache随material ab存储,多个引用了一样shader(变种)的材质会重复存储cache。
更新测试五
使用ShaderVariantCollection,记录全部用到的variant。
将SVC和shader打入一个ShaderAB。
将材质打成MaterialAB
运行时加载ShaderAB,取SVC,WarmUp,再加载MaterialAB。结果丢失部分variant
更换分包方式,SVC、shader和Material打成一个包。一切正常。
结论五:
·使用ShaderVariantCollection能够作到带变种的材质更新。
·目前版本(5.6.0和5.5.2)依然有bug,必须将SVC、shader和全部对应材质放在一块儿才能作到可热更新。
备注:
·为了测试,我用HFS配置了局域网http服务器,只要在同一个无线网端下,pc和手机都能访问,配合不一样平台的AB分文件夹管理,全部平台都能同步看到效果。
·ab.Unload()会把ab设为null!
·cginc头文件修改后,全部用到的shader必须手动import一次以强制从新编译!
参考资料:
·hfs使用介绍:http://bbs.feng.com/read-htm-tid-2234118.html
·ab模型:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html
·www禁用cache方法:http://answers.unity3d.com/questions/209078/disable-cache-for-www.html