在unity的资源中,shader是比较特殊的一类。主要有下面几个疑问
1. Shader算是代码,并且需要编译。那么是否可以热更新?
2. AB中加载进来的shader是否可以通过shader.Find(名称)来索引?
3.在使用shader_feature关键字后,build时忽略的变种是否要在运行时编译?
4.预编译shader cache的存储位置究竟在哪里?
针对以上问题我做了一系列测试,记录如下:
测试一:
准备一个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。

备注:
·为了测试,我用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
原文:http://www.cnblogs.com/cpxnet/p/6439706.html