Unity纹理与动画优化

这是Unity性能优化的资源优化第二篇,主要是Texture优化和Animation优化,其实在 SUNTAIL - Stylized Fantasy Village 这个工程里面纹理优化的空间挺大的,主要优化方式是纹理压缩。之前专门调研过各种纹理压缩格式在Android设备上的支持情况(主要是部分OpenGL ES3.0的不支持ASTC压缩),顺便也记录在这里了。

Texture纹理优化

纹理基本概念

纹理类型

Default:默认的纹理类型格式

Normal map:法线贴图,可将颜色通道转换为适合实时法线贴图格式

Editor GUI and Legacy GUI:在编辑器GUI控件上使用纹理请选择此类型

Sprite(2D and UI):在2D游戏中使用的精灵(Sprite)或UGUI使用的纹理请选择此类型

Cursor:鼠标光标自定义纹理类型

Cookie:用于光照Cookie剪影类型的纹理

Lightmap:光照贴图类型的纹理,编码格式取决于不同的平台

Single Channel:如果原始图片文件只有一个通道,请选择此类型

纹理大小选择

纹理的大小应该尽量设置为2的幂次方,不要求长宽一致,比如1024 * 512,如果不是2的幂次方,在导入Unity中时也会被强行填充为2的幂次方,会造成纹理浪费。纹理大小会直接影响内存与显存的占用大小,也会对GPU纹理采样造成更高负载、CPU负载、占用更高的显存带宽,所以需要选择适合的纹理大小。

选择合适纹理大小应尽量遵循以下经验:

  • 不同平台、不同硬件配置选择不同的纹理大小,Unity下可以采用bundle变体设置多套资源、通过Mipmap限制不同平台加载不同level层级的贴图。

  • 根据纹理用途的不同选择不同的纹理加载方式,如流式纹理加载Texture Streaming、稀疏纹理Sparse Texture、虚拟纹理VirtualTexture等方式。

  • 不能让美术人员通过增加纹理大小的方式增加细节,可以选择细节贴图DetailMap或增加高反差保留的方式。

  • 在不降低视觉效果的情况下尽量减小贴图大小,最好的方式是纹理映射的每一个纹素的大小正好符合屏幕上显示像素的大小,如果纹理小了会造成欠采样,纹理显示模糊,如果纹理大了会造成过采样,纹理显示噪点。这一点做到完美平衡很难保障,可以充分利用Unity编辑器下SceneView->DrawMode->Mipmap来查看在游戏摄像机视角下哪些纹理过采样,哪些纹理欠采样,进而来调整纹理大小。

可以用buildin管线,通过mip map视图查看哪些纹理过采样,哪些纹理欠采样。

纹理颜色空间

默认大多数图像处理工具都会使用sRGB颜色空间处理和导出纹理。但如果你的纹理不是用作颜色信息的话,那就不要使用sRGB空间,如金属度贴图、粗糙度贴图、高度图或者法线贴图等。一旦这些纹理使用sRGB空间会造成视觉表现错误。

纹理压缩格式

纹理压缩是指图像压缩算法,保持贴图视觉质量的同时,尽量减小纹理数据的大小。默认情况下我们的纹理原始格式采用PNG或TGA这类通用文件格式,但与专用图像格式相比他们访问和采样速度都比较慢,无法通用GPU硬件加速,同时纹理数据量大,占用内存较高。所以在渲染中我们会采用一些硬件支持的纹理压缩格式,如ASTC 、ETC、ETC2、DXT等。

另外前段时间调研了几种目前主流纹理压缩格式,结论如下:

Android:用ETC2没有什么问题;至于ASTC在Android 5.0/OpenGL ES 3.1后支持,市场大部分机型都支持(98.5%),部分OpenGL ES 3.0支持,不支持的主要是以下机型(不方便找,在此记录一下):

纹理图集

纹理图集是一系列小纹理图像的集合,优点如下:

1、采用共同纹理图集的多个静态网格资源可以进行静态合批处理,减少DrawCall调用次数。

2、纹理图集可以减少碎纹理过多,因为他们打包在一个图集里,通过压缩可以更有效的利用压缩,降低纹理的内存成本和冗余数据。

纹理图集的缺点就是美术需要合理规划模型,并且要求模型有相同的材质着色器,或需要制作通道图去区分不同材质,制作和修改成本较高。

纹理过滤

  • Nearest Point Filtering:临近点采样过滤最简单、计算量最小的纹理过滤形式,但在近距离观察时,纹理会呈现块状。

  • Bilinear Filtering:双线性采样过滤会对临近纹素采样并插值化处理,对纹理像素进行着色。双线性过滤会让像素看上去平滑渐变,但近距离观察时,纹理会变得模糊。

  • Trilinear Filtering:三线性过滤除与双线性过滤相同部分外,还增加了Mipmap等级之间的采样差值混合,用来平滑过度消除Mipmap之间的明显变化。

  • Anisotropic Filtering:各向异性过滤可以改善纹理在倾斜角度下的视觉效果,跟适合用于地表纹理。

纹理MipMap

逐级减低分辨率来保存纹理副本。相当于生成了纹理LOD,渲染纹理时,将根据像素在屏幕中占据的纹理空间大小选择合适的Mipmap级别进行采样。这样GPU不需要在远距离上对对象进行全分辨率纹理采样,因此可以提高纹理采样性能。同时也解决了远距离下的过采样导致的噪点问题,提高的纹理渲染质量。

MipMap的缺点在于Mipmap纹理要生成低分辨率副本,会造成额外的内存开销。但是通过Streaming加载机制可以加载目前需要的纹理。

Texture其它属性

Texture Shape

  • 2D 最常用的2D纹理,默认选项
  • Cube 一般用于天空和与反射探针,默认支持Default、Normal、Single Channel几种类型纹理,可以通过Assets > Create > Legacy > Cubemap 生成,也可以通过C#代码 Camera.RenderToCubemap在脚本中生成
  • 2D Array 2D纹理数组,可以极大提高大量相同大小和格式的纹理访问效率,但需要特定平台支持,可以通过引擎SystemInfo.supports2DArrayTextures 接口运行时查看是否支持。
  • 3D 通过纹理位图方式存储或传递一些3D结构话数据,一般用于体积仿真,如雾效、噪声、体积数据、距离场、动画数据等信息,可以外部导入,也可运行时程序化创建。

Alpha Source

默认选择Input Texture Alpha就好,如果确定不使用原图中的Alpha通道,可以选择None。另外From Gray Scale我们一般不会选用

Alpha Is Transparency

指定Alpha通道是否开启半透明,如果位图像素不关心是否要半透明可以不开启此选项。这样Alpha信息只需要占1bit。节省内存

Ignore Png file gamma

是否忽略png文件中的gamma属性,这个选项是否忽略取决于png文件中设置不同gamma属性导致的显示不正常,一般原图制作流程没有特殊设置,这个选项一般默认就好。

Read/Write

开启此选项会导致内存量增加一倍,默认我们都是不开启,除非你的脚本逻辑中需要动态读写该纹理时需要打开此选项。

Generate Mip Maps

什么时候不需要生成MipMaps?

1、2D场景

2、固定视角,摄像机无法缩放远近

  • Border Mip Maps 默认不开启,只有当纹理的是Light Cookies类型时,开启此选项来避免colors bleeding现象导致颜色渗透到较低级别的Mip Level纹理边缘上
  • MipMap Filtering
    • Box 最简单,随尺寸减小,Mipmap纹理变得平滑模糊
    • Kaiser,避免平滑模糊的锐化过滤算法。
  • Mip Maps Preserve Coverage,只有需要纹理在开启mipmap后也需要做Alpha Coverage时开启。默认不开启。
  • Fadeout MipMaps, 纹理Mipmap随Mip层级淡化为灰色,一般不开启,只有在雾效较大时开启不影响视觉效果。

选择合适纹理过滤的最佳经验

  • 使用双线性过滤平衡性能和视觉质量。

  • 有选择地使用三线性过滤,因为与双线性过滤相比,它需要更多的内存带宽。

  • 使用双线性和 2x 各向异性过滤,而不是三线性和 1x 各向异性过滤,因为这样做不仅视觉效果更好,而且性能也更高。

  • 保持较低的各向异性级别,仅对关键游戏资源使用高于 2 的级别。

其他可能有问题的纹理类型

  • 纹理图集大小设置不合理,图集利用率低

  • 大量只有颜色差异的图片

  • UI背景贴图而不采用9宫格缩放的图

  • 纯色图没有使用Single Channel

  • 不合理的半透明UI,占据大量屏幕区域,造成Overdraw开销

  • 大量2D序列帧动画,而且图片大,还不打图集

  • 不合理的通道图利用方案

  • 大量渐变色贴图,没有采用1像素过渡图,也不采用Single Channel, 粒子特效中较为常见。

根据检测报告进行纹理优化

优化前后的对比,可见纹理经过压缩,去除了不必要的Alpha通道,内存节约了200M左右,对于这个项目纹理优化效果显著:

Animation优化

Animation Type 四种类型:

  • None 无动画

  • Legacy 旧版动画,建议不要使用

  • Generic 通用骨骼框架:非人形动画选择Generic,在骨骼数差不多的情况下,Generic Rig会比Humanoid Rig省30%甚至更多的CPU时间

  • Humanoid 人形骨骼框架:人形动画才使用,人形动画需要Kinematices或Animation Retargeting功能,或者没有有自定义骨骼对象时选择Humanoid Rig

Skin Weights

默认4根骨头,但对于一些不重要的动画对象可以减少到1根,节省计算量;

Optimize Bones

建议开启,在导入时自动剔除没有蒙皮顶点的骨骼

Optimize Game Objects

在Avatar和Animatior组件中删除导入游戏角色对象的变换层级结构,而使用Unity动画内部结构骨骼,消减骨骼transform带来的性能开销。可以提高角色动画性能, 但有些情况下会造成角色动画错误,这个选项可以尝试开启但要看表现效果而定。注意如果你的角色是可以换装的,在导入时不要开启此选项,但在换装后在运行时在代码中通过调用AnimatorUtility.OptimizeTransformHierarchy接口仍然可以达到此选项效果。

Animation标签页

Resmple Curves:

将动画曲线重新采样为四元数数值,并为动画每帧生成一个新的四元数关键帧,仅当导入动画文件包含尤拉曲线时才会显示此选项

Anim.Compression:

  • Off 不压缩,质量最高,内存消耗最大

  • Keyframe Reduction 减少冗余关键帧,减小动画文件大小和内存大小。

  • Keyframe Reduction and Compression 减小关键帧的同时对关键帧存储数据进行压缩,只影响文件大小。

  • Optimal,仅适用于Generic与Humanoide动画类型,Unity决定如何进行压缩。

Animation Custom Properties:

导入用户自定义属性,一般对应DCC工具中的extraUserProperties字段中定义的数据

动画曲线数据信息

  • Curves Pos**:** 位置曲线

  • Quaternion**:** 四元数曲线 Resample Curves开启会有

  • Euler**:** 欧拉曲线

  • Scale**:** 缩放曲线

  • Muscles**:** 肌肉曲线,Humanoid类型下会有

  • Generic**:** 一般属性动画曲线,如颜色,材质等

  • PPtr:精灵动画曲线,一般2D系统下会有

  • Curves Total: 曲线总数

  • Constant**:** 优化为常数的曲线

  • Dense**:** 使用了密集数据(线性插值后的离散值)存储

  • Stream**:** 使用了流式数据(插值的时间和切线数据)存储

设置优化后查看原则

一看效果差异(与原始制作动画差异是否明显)

二看曲线数量(总曲线数量与各种曲线数显,常量曲线比重大更好)

三看动画文件大小(以移动平台为例,动画文件在小几百k或更少为合理,查过1M以上的动画文件考虑是否进行了合理优化)

参考资料

主要内容来自Bilibili的Metaverse大衍神君的 2022新年计划《Unity性能优化》 ,其中的大部分笔记摘抄自其PPT作为以后参考。