在上一期《粒子系统优化:Mesh 模式下的优化策略》中,我们针对粒子系统中和网格相关的优化注意点和大家做了解释。这些看似细小的知识点,很容易在大家的开发和学习过程中被疏忽,而长期的问题积累最终都会反映到项目的性能表现上。为此,我们将这些规则列出,并且以一个个知识点的形式向大家逐一解读。

本期,我们将讲解UWA 本地资源检测中针对预制体(Prefab)进行检测的规则:“使用了 MotionVector 的 SkinnedMeshRenderer”、“使用 Tiled 模式的 Image 组件” 和 “不可见的 Image 组件”。我们将力图以浅显易懂的表达,让职场萌新或优化萌新能够深入理解。

1、使用了 MotionVector 的 SkinnedMeshRenderer

首先我们先理解一下 Unity 里面的 “骨骼动画”。它的概念很贴近现实中的 “骨骼” 的含义:常见的模型被分解为两个部分:骨骼 Bones 和蒙皮 Mesh。这些以 “关节” 结构连接在一起的骨头为模型构建起了一套骨架;随着骨架结构的运动变化,模型表面的蒙皮网格也会随着骨架的变动而发生改变。

这种以骨骼位置变动为核心的动画就是骨骼动画。相较于关键帧动画和关节动画,Bone animations 目前在 Unity 中应用极其广泛。

在 Unity 中,如果导入的模型有骨骼动画,那么 Unity 就会自动为对应的网格添加上 Skinned Mesh Renderer,来渲染这些骨骼动画。蒙皮网格渲染器组件会将模型的网格 (Mesh) 与骨骼节点绑定起来,当骨骼动画播放时,网格的顶点会随着它所绑定的骨骼进行运动,这便形成了 “皮肤” 随骨骼运动的效果。在 Inspector 界面可以对该组件进行一系列配置。

其中有一项属性就是本条的主角:Skinned Motion Vector。

运动向量(Motion Vector) 的概念来源于视频压缩领域,在维基百科中有如下解释:

In video compression, a motion vector is the key element in the motion estimation process. It is used to represent a macroblock in a picture based on the position of this macroblock (or a similar one) in another picture, called the reference picture.

通俗地理解,运动向量描述了在屏幕空间中,每个像素所对应的物体在连续两帧之间的运动 “速度”,该 “速度” 值等于该像素块在这帧的位置减去在上一帧的位置,也就是当前像素下的片元在相邻两帧之间,屏幕空间位置的差。运动向量的示意图如下:


来源:维基百科

Unity 中,在 Mesh Renderer 与 Skinned Mesh Renderer 组件中都可以开启 Motion Vector,用以获取物体的运动信息。若开启,相机会在渲染管线中增加一个 motion vector pass,将运动向量渲染在一个 Buffer 当中,用户可以将 Shader 脚本中这个 Buffer 用于后处理特效。

Motion Vector 一般会用于运动模糊这样的后处理特效、基于时间的抗锯齿算法(temporal antialiasing)等。但是 Motion Vector 带来的显存中的双倍 buffer 开销和运算量开销是不容忽视的。而 Skinned Motion Vector 在移动端项目当中的应用也是很少的,一般不建议开启。

所以,在本条规则筛选出对应的 Skinned Mesh Renderer 后,开发团队需要根据实际情况决定是否勾选对应骨骼动画的 Skinned Motion Vector。


2、使用 Tiled 模式的 Image 组件

首先我们需要了解 UGUI,一言以蔽之:UGUI 是 Unity 官方开发的 UI 系统。对它的理解,要放大到最基本的 “界面” 的含义:它能干很多事,常见的功能菜单,打开的按钮,主界面上的模块的显示,等等,都会用到这个 UGUI。

而 Image 组件,也就是图像组件,最常用于显示非相互性的图像,相信大家在日常的开发过程中并不陌生,我们就不再展开讲了。在我们将贴图(必须是 Sprite 哦)拖入 Source Image 后,我们就可以在 Inspector 面板设置 Image Type。

此时有一种情况很容易被大家忽略:当 Image.Type 为 Tiled 时,如果我们使用的 Texture 的 WrapMode 不是 Repeat,那么 Unity 在处理时,就会通过生成多个面片的方式来实现平铺效果。

在 Image.Type 为 Tiled 模式的前提下,两图的左侧是 Texture 为 Repeat 模式的效果展示与网格显示;右侧是 Texture 为 Clamp 模式的效果展示与网格显示。

这样我们就无意间增加了面片数量。一旦我们在 Image 组件里我们不注意这个问题,那么量变引起质变,在整个项目里这些额外多出来的面片就会在运行时导致额外的计算压力和渲染消耗。

所以在找出这些使用了 Tiled 模式的 Image 组件后,开发团队需要对导入的 Texture 的 WrapMode 和 Image 组件的 Type 类型进行进一步的检查,以避免上述情况的发生。


3、不可见的 Image 组件

本条规则其实和上一条类似,问题的着眼点都是导入到 Image 组件内的 Texture 资源。在之前的文章《纹理优化:不仅仅是一张图片那么简单》中,我们对 Alpha 通道有过简单的描述。

此处我们谈论一下 Alpha 值全为 0 的情况。Alpha 值恒为 0,说明 Texture 为全透明状态,视觉上的纹理表现其实为 “不存在、看不见”,但对项目和计算而言,它依然存在并占有相关的内存和开销。

当我们把全透明的 Texture 导入到相关 Image 组件后,Image 组件也会表现为 “视觉上消失,物理上存在”,这种情况下,图像组件没有对项目产生展示效果上的贡献,反而带来了性能上额外的占用与计算消耗。

所以借助本条规则的筛选,开发团队可以最大限度地避免这种情况的发生。当然,我们不能武断地排除 Image 组件不可视化的特殊应用,开发团队还是要根据项目的实际需求来决定这些 Image 组件的 “生与死”。


希望以上这些知识点能在实际的开发过程中为大家带来帮助。大家其实可以发现,看似各个独立的部分:网格、粒子系统、材质和纹理等,其实在优化上都有着互相的关联。所以面对项目优化,开发团队要以一个整体的高度去看待项目的各个部分。

需要说明的是,每一项检测规则的阈值都可以由开发团队依据自身项目的实际需求去设置合适的阈值范围,这也是本地资源检测的一大特点。同时,也欢迎大家来使用 UWA 推出的本地资源检测服务,可帮助大家尽早对项目建立科学的美术规范

往期优化规则,我们也将持续更新。
《动画优化:关于 AnimationClip 的三两事》
《材质优化:如何正确处理纹理和材质的关系》
《纹理优化:让你的纹理也 “瘦” 下来》
《纹理优化:不仅仅是一张图片那么简单》

万行代码屹立不倒,全靠基础掌握得好!

性能黑榜相关阅读

《那些年给性能埋过的坑,你跳了吗?》
《那些年给性能埋过的坑,你跳了吗?(第二弹)》
《掌握了这些规则,你已经战胜了 80% 的对手!》


↙↙↙阅读原文可查看相关链接,并与作者交流