4

Shader打AssetBundle包变体丢失问题

 2 years ago
source link: https://blog.uwa4d.com/archives/TechSharing_271.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1)Shader打AssetBundle包变体丢失问题
​2)Unity升级后在iOS平台的贴图导入问题
3)字体文件AssetBundle包崩溃
4)给主相机设置RT的注意事项
5)FBX的导入WeldVertices的设置问题


这是第271篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

AssetBundle

Q:Unity官方后处理PostProcessing,进行AssetBundle打包时,变体太多(4000+),修改了uber.shader,屏蔽了项目中不可能使用的关键字(后期要使用也可以通过热更修改)。

AssetBundle打包后,发现变体没完全打包,Editor下查看Shader变体数量是300+,打包后使用AssetStudio查看AssetBundle,发现只打了100+的变体,大部分丢失。运行通过断点调试也验证了所有Key均设置,FrameDebug中也丢失了一些关键字。

uber.shader下所有关键字均是multi_compile 定义,理论上multi_compile定义的关键字都会全编译所有组合,但是打包后出现丢失。

A:在进行单元测试中,在一个空项目中打包uber.shader,发现打包后,变体正确。但是开发项目和打包项目依旧生成不正确。怀疑是Unity缓存原因。于是删除Library目录下shaderCache,再次打包,还是不正确。再次删除已生成的AssetBundle资源,完全重新生成,终于正确。

所以multi_compile定义的关键字打包出现问题,需要删除缓存和已生成的AssetBundle才能最终正确生成新的AssetBundle。

感谢题主1 9 7 3-311135@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6167ff118f8c834241dc7777


Asset

Q:我们一直用贴图后处理脚本对贴图进行导入,在iOS平台使用ASTC_6x6格式。但是升级到Unity 2019,打开iOS工程,经同步缓存和导入完成后,大部分贴图的预览界面上并没有显示压缩格式和压缩后的尺寸,而是显示no yet compressed,似乎导入操作没有完成。Android平台并没出问题。

这样的贴图会显示模糊,并且打出的Bundle还会更大。手动Reimport可以解决。但是如果另外建立一份工程拷贝,则又会出现同样的问题。求问有什么解决方法?

A1:经查,是我这边的纹理资源导入后处理脚本(iOS)有问题,但是其在Unity 2018下不会体现出来,但是在Unity 2019中会直接让某些贴图的导入工作无法完成。由于使用了 Unity Accelerator(也就是Asset Pipeline v2对应的缓存服务器),重新导入也不会生成新的缓存,只能让进行重新导入的项目局部正常。

我们的导入后处理脚本中在iOS下,对JPG单独使用了不带透明通道的ATSC压缩。在Unity 2019中,ATSC压缩不再区分是否带透明通道。修改脚本+删除Unity Accelerator缓存+重新导入某个Working copy(同时也重建了缓存)可解决。

注意:上述无法导入完成的情况,打出包来会有两个明显的问题:包体变大、图片模糊。

感谢题主加菲教主@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/614ee1bc8f8c834241ba925e


AssetBundle

Q:AssetBundle打包参数BuildAssetBundleOptions.DisableWriteTypeTree。会极大缩减AssetBundle包大小,同时也带来运行时SerializaFile相同数量级下内存占用缩减一半左右,并提高加载速度。

官方说明比较简单:如果开启DisableWriteTypeTree选项,则可能造成AssetBundle对Unity版本的兼容问题。基于其带来的运行时优势,还是决定使用该选项。

导出测试包一切运行正常,热更新也运行正常,本来以为该优化就完结。但是后来发现相同的AssetBundle资源,在编辑器下AssetBundle运行会崩溃(必然出现),导出的测试包(所有平台)一切正常。(该问题必须要解决,因为游戏发布后线上的问题可以在本地调试复现,如果本地不能运行AssetBundle包或者运行不同的AssetBundle包原则上不允许。)

A:开始排查问题:首先定位到崩溃的资源,是TMPTextPro相关的字体资源包(复合包,包含了多个资源内容)。

验证BuildAssetBundleOptions.DisableWriteTypeTree的影响,屏蔽BuildAssetBundleOptions.DisableWriteTypeTree打包参数。打包后编辑器运行不崩溃,肯定了该参数造成的问题。(这里当时猜测是Unity内部代码的某个宏导致的,导致了在Editor下和发布时走了不同的解析代码,但是又没有源代码,无法具体验证。)

开始排除问题:
首先排除Unity的编辑器缓存造成的该问题,重置后依旧崩溃。

开始拆解问题包,将所有资源内容单个打包(一个资源对应一个包)。最后精确定位到是TMPTextPro的字体FontAsset资源包调用AssetBundle.LoadAsset()方法时崩溃。

分析问题:设置DisableWriteTypeTree参数将不在AssetBundle中包含相关的文件TypeTree信息,是因为没有该信息后导致的反序列化不正确,为什么其它资源能正常解析,就单单FontAsset会出现问题?打开TMP_FontAsset.cs源码,查看发现这么一段代码。

#if UNITY_EDITOR
///


/// Persistent reference to the source font file maintained in the editor.
///

[SerializeField]
internal Font m_SourceFontFile_EditorRef;
#endif

发现它居然在可序列化类中加入了条件编译,造成导出运行和编辑器运行该类的实现不一样。

这就是为什么导出包运行正确,编辑器崩溃的原因。因为AssetBundle打包时的UNITY_EDITOR是不成立的,会设置对应平台的宏。所以打包时FontAsset资源的数据是不包含m_SourceFontFile_EditorRef该属性的序列化信息,未开启DisableWriteTypeTree时,因为有TypeTree信息,编辑下运行程序能通过TypeTree信息正确反序列化,当设置DisableWriteTypeTree时,反序列化失败,直接崩溃。

解决:给属性m_SourceFontFile_EditorRef添加标签[NonSerialized],再对应修改一些Editor代码,让功能在Editor下也正确。

感谢题主1 9 7 3-311135@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/616a8e668f8c834241dfd78b


Rendering

Q:用setTargetBuffers方法将一张低分辨率RT设置给主相机。在屏幕分辨率不变的情况下,对场景相机做了降级。然后出现了一个问题是:所有的后处理都失效了,查看后发现所有OnRenderImage方法的Source都为null(我理解为没有设置Rendertarget,默认为屏幕)。我将上面那张FrameBuffer强行代替Source传入后处理,查看FramedeBugger,后处理几个Draw Call能正常渲染,但是仍旧不能对最终效果生效,也就说没有写入到上面的FrameBuffer。

我之前的解决方法是魔改所有OnRenderImage,传入FrameBuffer代替Source,输出到另外一张FinalBuffer,最后再绑定到屏幕上,下面是测试代码。

1.png
2.png
3.png

但项目中后处理比较多,且ImageBuffer仍存在后处理中,无端增加了RenderTexture的开销。

现在的处理方法是直接设置TargetTexture,但逻辑操作上会绕一层。想问下引起上面的原因,或者说有什么更好的解决方法?

环境是2019.4.3f + built-in + OpengES3.0,然后再用CommandBuffer将低分辨率适配到高分辨率。

A1:实践下来,建议还是用CommandBuffer来操作,比较灵活:

  1. OnPreRender阶段先设置好TargetBuffer(ColorBuffer:RTA、DepthBuffer:RTB)。
  2. 使用CommandBuffer在CameraEvent.BeforeImageEffects或者AfterImageEffects这个事件上做所有的后处理。
  3. 核心在于第一次Blit的Source为ColorBuffer,最后一次Blit的Destination为BuiltinRenderTextureType.CameraTarget,中间可以穿插任何后处理效果。
    4.png

而且如果发现改了ColorBuffer的分辨率,画面没有正常缩放,需要第二步后处理的CameraEvent改为AfterEverything:

5.png

还有就是如果改了TargetBuffer,需要对SceneCamera做特殊处理。

感谢范世青@UWA问答社区提供了回答

A2:下图是这两种方法的实验对比,至于两者区别的根本原因,是其方法内部实现的差异:SetTargetBuffers的实现过程是没有设置渲染目标(也就是屏幕)的RenderTexture的,这也就是为何用该方法时OnRenderImage中的Source是null。

6.png

该问答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/61518a318f8c834241be8ac3


Asset

Q:问下在FBX的导入设置里面,weldVertices是需要设置为True还是False?

A:建议设置为True。相同顶点会被合并,可以减少内存和Mesh渲染的压力。

感谢萧小俊@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6164066a8f8c834241d70ffa

20211018
更多精彩问题等你回答~

封面图来源于网络


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK