Unity中光照图错乱问题研究

Unity中光照图错乱问题研究

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

UWA 问答社区:answer.uwa4d.com
UWA QQ群:465082844(仅限技术交流)


Lightmap

Q:我来分享下Unity项目中可能会引起光照图错乱的问题及其处理方式。
近期项目使用Unity 2017 版本升级后,团队中某些人的机器光照图总是不正确,而有的人是正确的。我们一开始怀疑模型资源的问题,检查了下资源和导出设置,是有些问题但不是引起错乱的原因,后来看到光照图错乱和正确的机器上,同一个模型的UV确实完全不一样。

后来终于确认原因并且可以复现:Unity导入模型会做顶点优化,顶点数并不是和Max 中一致但是面数绝对一致,重点是不同Unity 版本即使同一个模型优化过的顶点数也不一致(应该是算法不同的问题),顶点数不同自然光照图 UV 分布生成的也不同。所以比如在Unity 2017.3中导入一个模型并生成光照图,导出成Package并导入到 Unity 2017.4或者其它版本,会发现光照图 UV 错乱,因为重新导入的模型顶点和光照图 UV 都不同了。所以同一个团队中的美术同学一定要和开发人员使用相同版本。

这里有一个更隐蔽的情况:大家都使用同一个Unity版本,但是不同的机器依然会出现某些人光照图错乱的问题。表现正常的机器是由于使用低版本的Unity烘焙完光照图或者更新了别人烘焙完的光照图后,然后直接用高版本的Unity 打开这样是不存在问题的;如果同一个项目删掉Library后,再用高版本的 Unity 重新打开这时候就不对了。究其原因是因为不删除Library升级项目每个模型不会被重新导入,顶点数维持在老版本导入的结果,一旦删除Unity就会重新按照新版本来导入并生成顶点数,当然其实你在新版本中修改模型的设置,也会导致按照新版本来重新导入模型而发生光照图错乱。

以上就是所有原因,并可以轻松复现,结论就是:
1)建议团队中所有人使用同版本的Unity开发;
2)升级过程中需要删除本地的 Library 然后再用新版本Unity打开。

希望其它的朋友可以避过这个坑,在此感谢钱康来@UWA问答社区在查找过程中的帮助:)

感谢Yaukey@UWA问答社区分享了该经验,欢迎大家转至社区进一步交流:
https://answer.uwa4d.com/question/5b4ed6e61e88b37d34e650a4


UGUI

Q2:之前了解过UGUI的画布上的图片或者文字发生改变,画布会重新算一遍UV,重新合并图集,以达到减少DrawCall的目的,所以为了减少画布重新绘制带来的性能问题,我把经常变动的UI放到一个动态画布上,不频繁改变的放到静态画布上。这里产生了一点疑惑:如果我一个UI窗口制作成一个预设,这个UI有动有静的,那我是将这个UI放到静态画布上还是动态画布上好,这个怎么放?

另外,不在同一个画布下面,UI之前的层级关系怎么处理?比如画布A放静态,画布B放动态,现在有一个UI叫C,放到A,一个UI叫D放到B;如果画布B是挡在画布A前面,但是我需要UI C挡在UI D前面,这种怎么处理?

题主的提问里有些描述可能不太准确,下面先简单说一下:“重新算一遍UV”的操作是发生在Canvas.SendWillRenderCanvases这个函数中的,发生变化(改图片、改文本、改Alpha等)的UI元素越多,开销就越大,通过“动静分离”是优化不了这部分的开销的,这部分开销需要从控制变化频率来入手。

“动静分离”主要是用来优化Canvas.BuildBatch的开销,这部分开销产生的原因是:当某个Canvas里任何元素发生任何变化(图片、文字、alpha、位置、缩放等等…)的时候,该Canvas就需要进行合并Mesh的操作。如果Canvas比较复杂,耗时就会比较高。
而在进行“动静分离”时,理论上是需要根据Canvas的复杂度来规划的,个人觉得Canvas的总顶点数可以作为一个简单的指标。以下用几个例子来说,可能比较直观一些:
对于一个常见的MMO项目来说,常规做法是会把技能面板,任务面板独立划分Canvas。其中,技能面板即使是在读CD的时候,一般来说顶点数也不太会超过1000个,UI元素数量也不会太多,所以即使这个面板里有动态元素,也有静态元素,但其实不太需要“动静分离”,因为这样的面板BuildBatch的开销不太明显。
而任务面板中,由于条目比较多,产生的顶点数通常也会很高,2000以上也是很常见的,所以这个面板里如果存在动态的UI元素(比如一个高亮效果的序列帧动画),那就会引起耗时较高的BuildBatch操作,那么这个面板就需要“动静分离”了。
具体操作时,“动静分离”可能也会遇到一些纠结的地方,因为零碎的动态UI如果都拆成Canvas的话,很容易导致DrawCall的显著上升,前后遮挡也会不好控制,所以推荐@贾伟昊的这篇文章:使用Shader对UGUI进行优化。简单说就是通过Shader来做UI动画,直接避免Canvas.SendWill和Canvas.BuildBatch的开销,具体就不介绍了,文章写得很详细了 :)

该回答由UWA提供,欢迎大家转至进一步交流:
https://answer.uwa4d.com/question/5b514cf21e88b37d34e650e9


资源管理

Q3:我想在Unity 4.x版本下用AssetBundle实现零冗余,但在实现的时候遇到一个问题不知道该如何解决。例如:

BuildPipeline.PushAssetDependencies()
BuildPipeline.BuildAssetBundle 打包TextureA
BuildPipeline.PushAssetDependencies()
BuildPipeline.BuildAssetBundle打包A资源
BuildPipeline.PopAssetDependencies()

这时我需要打包一个B资源,B资源中引用了Texture A并且引用了Texture B,而且Texutre B又被C资源引用,C资源又不需要Texture A,这时逻辑需要如何调整才能实现资源 A和资源 B依赖Texture A,然后资源 B依赖Texture A和Texture B,资源 C Texture B和其他资源呢?

恩,是的,Unity4.x的AssetBundle打包确实有这样的弊端,但还是有方法的,就是分步打包。
即先通过下面的方式让资源A和B依赖TextureA:
Push
Build TextureA
Push
Build 资源A和资源B
Pop
Pop

然后,通过下面方式重新打包资源B和TextureA,并第一次打包TextureB:
Push
Build TextureA和TextureB
Push
Build 资源B
Pop
Pop

对于后续的资源 C来说也是如此。这个时候你肯定会想这不是打出了两个Texture A?
是的,没错,这里需要有个注意的地方,就是在Unity 4.x中一定要把Deterministic Option打开,当开启这个选项后,上述方法才能奏效。即让第二次打出来的TextureA的AssetBundle直接覆盖第一次的TextureA AssetBundle,由于是Deterministic的,所以它们的Hashid是一致的,这样既可以保证资源A的AssetBundle与新的TextureA的AssetBundle有依赖关系,同时还可以做到资源B与Texture A和B都有依赖关系。
通过以上方式,题主就可以打出零冗余的AssetBundle文件了:)加油!

该回答由UWA提供,欢迎大家转至社区进一步交流:
https://answer.uwa4d.com/question/5b45b5222a652c518d8d539e


资源管理

Q4:Unity 5.5.4p5版本采用AssetBundle加载资源(有依赖),有很大几率出现如下错误,怎么处理呢?

“The referenced script on this Behavior is missing!”, “The referenced script on this Behavior (Game Object ‘Prefab 1’) is missing!” and “GameObject (named ‘Prefab 1’) references runtime script in scene file. Fixing!”

“The referenced script on this Behavior is missing!”,如果是Unity 5.0以后的打包方式,那么原因很可能是:脚本的序列化信息变化了,需要重新打包AssetBundle来修复这个问题。
而如果是Unity 4.x的老版本打包方式,那么除上述原因外,还有一种可能是脚本处于依赖的AssetBundle中,而后续AssetBundle加载时,其依赖的AssetBundle还未加载成功或者已经被卸载,那么也会出现该问题。

该回答由UWA提供,欢迎大家转至社区进行进一步交流:
https://answer.uwa4d.com/question/5b46bcb9339d267d357c6aef


UGUI

Q5:Unity 5.5.6版本中Texture import Settings中选Sprite类型,Sprite mode中Mesh Type,如果作为UI图集应该使用哪种模式选择?

Full Rect。MeshType是给SpriteRenderer用的,会按照图片的形状生成一个近似的Mesh。但是对于UGUI来说都是一样的,都是按照ImageType来画Mesh,但是如果使用Tight的话,就意味着(可能)需要额外的内存,所以还是建议使用Full Rect。

感谢Chaos@UWA问答社区提供了回答,欢迎大家转至社区进一步交流:
https://answer.uwa4d.com/question/5b4422bbf91024518ab77aed


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

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
官方技术QQ群:465082844(仅限技术交流)