前言
本文仅作为性能优化的导读,依靠本文可以了解到一些Unity性能优化的方法,以及学习性能优化需要了解的知识,以此来入门如何性能优化。本文同时提供了很多参考阅读供读者阅读。
关于AOI,可参考我的另外一篇文章
关于Resources System,可参考这篇文章,资源优化中也有更加详细的内容
由于文章太长,性能优化上篇可参考这篇文章
资源优化
资源类型
- 模型
- 动作
- 纹理
- 声音
- 字体
- 场景
- 地形
- 光影
- UI
- 图集
- 粒子系统
资源制作规范和确定
注意:下面的标准仅供参考,主要还是美术资源需要有一个标准,每个项目的情况不同也会有不同的优化标准。
Mesh:
动态模型:面片数<3000,材质数<3,骨骼数<50,时长,帧率
静态模型:顶点数<500,UV,LOD
Audio:
长时间音乐(背景音乐): (压缩格式,不经常播放) mp3 压缩方式:Compressed In Memory
短时间音乐(音效): (非压缩格式,经常播放) wav 压缩方式:Decompressed on load
Texture:
贴图长宽<1024
格式要求
Shader:
尽量减少复杂数学运算,减少discard操作。
美术风格:
写实? 像素? 卡通?
视角:
2D/3D/2.5D
固定/自由
场景/地图:
无缝大地图?超大规模?
玩法:
单人/多人
休闲/棋牌/动作/冒险
更多:
制作优化
可读写(Read/WriteEnable):
开启后数据在内存中保存两次,GPU一次,CPU一次,因为大多数平台上GPU内存回读的速度极慢,所以将数据从GPU内存读入临时缓冲区以供CPU代码使用
如果不开启则数据内存中只保存一份。
实际开发中可以写一个工具来设置这个选项是否设置正确。
纹理压缩:
纹理压缩格式:
- ETC:最初是为移动设备开发的格式,如今是安卓标准压缩方案
- DXT:原名S3TC,由S3Graphics开发的一种与组块油管的有损算法,又被称为DXTn或者DXTC,主要用于PC。
- PVRTC:由Imagination公司专为PowerVR显卡核心设计,一般只用于苹果的设备,部分安卓支持
- ASTC:由ARM和AMD联合开发,一种新的压缩格式
- Crunch:一种基于DXT或ETC的有损压缩格式,构建慢但是读取快。
平台默认压缩格式:
- 安卓:
- RGB Compress ETC[RGB 24bit]
- RGBA Compress ETC[RGB 32bit]
- IOS
- RGB Compress PVRTC 4bit
- RGBA Compress PVRTC 4bit
如果平台不兼容指定的压缩格式会怎么办?
- 对资源进行重新解压为RGBA32(耗时)
- 对之前解压的不支持部分和新解压部分都储存(耗内存)
对尺寸有什么要求:
理想状态长宽都是2的幂,则符合POT格式。
如果尺寸不是2的幂,如果平台支持则纹理大小所需内存增加,采样可能比较慢;如果平台不支持,则对纹理进行缩放和填充达到下一个2的幂大小,占用更多内存,加载速度变慢
如何选择:
- 根据纹理用途,比如单通道,多通道
- 权衡文件的质量和大小(调整ResizeAlgorithm,可参考这篇文章的线性差值部分)
模型导入优化:
- 模型压缩(在保证可接受的质量的前提尽量选择压缩率高的)
- 网格优化(一般可以开启,在网格层面对GPU进行优化)
- 可读写(不需要操作模型的时候不用勾选)
- LightMap UV(自动为模型生成光照贴图 UV,不需要时不勾选)
- 动作导入(模型没动作的时候不用勾选)
动作导入优化:
- 动画压缩(减少关键帧进行压缩)
- Rig->优化游戏对象(动作的骨骼节点数减少为只有一个子节点)
纹理导入优化:
- 导入Unity支持的格式(BMP,TGA,GIF,JPG,PNG等)否则不支持二次处理
- 纹理格式(在保证可接受的质量的前提尽量选择压缩率高的)
- 制作成符合POT要求的形式(2的幂),否则就是NPOT格式,资源占用大
- 可读写
- Mipmap(2D的UI不需要开启)
- 纹理大小
- 压缩选项
UI制作优化:
- 能用九宫格就用九宫格
- 使用图集
多平台优化:
针对不同平台选定不同的设置比如资源的压缩格式
- Standalone
- IOS
- Android
- ......
减少冗余资源与重复资源:
- Resources目录下的资源不管是否被引用,都会被打包进安装包,不使用的资源不要放在Resources目录下。
- 不同目录下的相同资源文件,如果都被引用,那么也都会被打包进资源包,造成用余应该保证同一个资源文件在项目中只放在一个目录位置。
- 关于Resources System,具体可参考这篇文章
- 关于重复资源,可以参考Unity游戏性能优化重复资源的危害,重复资源如何查找
优化流程的自动化: 快速有效的进行资源监测和优化
继承AssetPostprocessor来制作自动化优化脚本
资源监测与分析
- 导入资源时进行检查,降低项目冗余度
- 使用前面前言中提到的Profiler等性能分析工具和方法
- 第三方检测,比如UWA是一家做性能优化的公司,其里面的工具可以对游戏安装包大小和性能做优化。
https://blog.csdn.net/uwa4d/article/details/73466142
GPU优化
优化方向
- 减少绘制的数目
- 保持材质的数目尽可能少。这使得Unity更容易进行批处理。
- 使用纹理图集(一张大贴图里包含了很多子贴图)来代替一系列单独的小贴图,它们可以更快地被加载,具有很少的状态转换,而且批处理更友好。
- 如果使用了纹理图集和共享材质,使用Renderer.sharedMaterial 来代替Renderer.material 。
- 使用光照纹理(lightmap)而非实时灯光。
- 使用LOD,好处就是对那些离得远,看不清的物体的细节可以忽略。
- 遮挡剔除(Occlusion culling)
- 使用mobile版的shader,因为简单。
- 动静分离(防止面片的重复渲染,其实是GPU优化,当然也减少了dc)
- 优化显存带宽
- OpenGL ES 2.0使用ETC1、PVRTC格式压缩等等,在打包设置那里都有。
- 使用mipmap
Unity优化之GPU优化:https://zhuanlan.zhihu.com/p/47056964
LOD 技术
LOD技术即Levels of Detail的简称,意为多细节层次。LOD技术指根据物体模型的节点在显示环境中所处的位置和重要度,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
在需要加入LOD的位置创建空物体并增加LODGroup组件,在对应的LODn中的Renderers中加入不同精细度的模型即可实现LOD分层,可通过拖拉鼠标调节每个LOD层级的占比。
https://docs.unity3d.com/Manual/LevelOfDetail.html
遮挡剔除Occlusion Culling
介绍:
只渲染视锥体内的物体,视锥体外或者是椎体后的不渲染。
视锥体剔除能减少顶点和面的个数,从而降低GPU的负担,和CPU的工作量,提高性能。
请注意,如果使用了 LOD 组,则 Unity 会使用静态遮挡物的基础细节级别游戏对象 (LOD0) 来确定要遮挡的对象。
如果游戏对象的轮廓在 LOD0 和其他 LOD 级别之间变化很大,这个游戏对象可能不适合设定为静态遮挡物。
要被设定为静态遮挡物,游戏对象必须满足以下条件:
- 具有 Terrain 或 Mesh Renderer 组件
- 不透明
- 在运行时不移动
遮挡剔除步骤:
- 选中要渲染的物体,将其Static属性设置:
- 静态遮挡物 (Static Occluder)(这些游戏对象不会移动,但会阻挡后面的游戏对象)或
- 静态被遮挡物 (Static Occludee)(这些游戏对象不会移动,但会被静态遮挡物遮挡)
- 一个游戏对象可以同时是静态遮挡物和静态被遮挡物
- Camera组件选择好Occlusion Culling选项为真
- 打开设置面板Window->Render->OcclusionCuling
- 选中面板的Visualzation,点击Bake.
- 选中Cameras,点击Bake
官方文档:https://docs.unity3d.com/Manual/OcclusionCulling.html
https://www.bilibili.com/video/BV1qt411i7sC?share_source=copy_web
光照贴图Lightmapping
介绍:
Unity中的光照贴图其实就是把场景中的静态物体做间接光的计算,并把计算结果存储到图片上,这些图片会附在相应的物体上,光照贴图会跟随相应的物体。
Lightmapping实现步骤:
1.首先确认渲染的物体,将其Static属性设置为LightMapStatic
2.设置参与Bake的渲染光源的Mode改为Baked
3.Window->Lighting->Setting->点击Generate Lighting,生成光照贴图
官方文档:https://docs.unity3d.com/cn/current/Manual/lighting-window.html
Mipmap
介绍:
- 如果纹理过大,会产生远处会出现摩尔纹,近处会出现锯齿,可以使用Mipmap来优化。
- Mipmap中每一个层级的小图都是主图的一个特定比例的缩小细节的复制品。
- 因为存了主图和它的那些缩小的复制品,所以内存占用会比之前大。
- 同时可以根据实际情况,选择适合的小图来渲染。
- 所以,虽然会消耗一些内存(33%),但是为了图片渲染的质量(比压缩要好),这种方式也是推荐的。
mipmap步骤:
- 在屏幕空间中取当前像素相邻的像素并查询其对应的uv坐标。
- 计算出当前像素点与其他像素点距离的最大值L。
- 根据最大值L通过上面公式计算得到该点所处的层数D。
https://www.lengyueling.cn/archives/37
什么时候用?
纹理在场景中是否有深度变化?
- 有变化:各种模型
- 没变化:游戏UI、SkyBox
有深度变化的情况按情况使用:
深度变化细微则不需要启用,如果深度变化打则可以使用
OverDraw
减少OverDraw区域:http://t.csdn.cn/mUjzx
介绍:
- 单位像素在单位时间内,被多次渲染
- UWA分析报告中,以总填充总数来表达一帧内渲染的像素数量,过多Overdraw可能会引起GPU过载,影响动画的播放和界面响应速度。
减少OverDraw: 优化OverDraw的方向就是尽可能的减少不同UI的重叠区域
- 使用rect mask 2d代替mask组件,因为mask组件自带2层overDraw
- Mask合批规则:mask内的元素可以和mask内的其他元素进行合批,不能和非mask元素进行合批
- 之所以不能合批,是因为不同的材质不能合批,mask用的是stencil模板材质,非mask用的是default uimat材质
- rectmask2d合批规则:只能合批自己遮罩范围内的ui对象,不会像mask一样额外生成两个drawcall
- 本质区别:mask是通过gpu模板剔除实现,rectmask2d是通过cpu顶点剔除实现的,因此如果当前GPU瓶颈,反而需要使用mask而不选择retmask2d
- 使用Text组件的OutLine和Shadow,Shadow会增加一层OverDraw,而OutLine是复制了四份Shadow实现的
- 对于弹出窗口,位于底层的窗口如果被上层遮挡,请将它从Camera渲染层级里移除并将不可见的Canvas设置enable = false,不在Camera渲染层级里的Canvas.enable = true,它下面的UI仍然会产生OverDraw
- UI上的特效粒子,尽量简单,如果可以请用序列帧动画实现
- 不使用空白或透明的Image,尽管alpha = 0,还是会渲染并增加一层OverDraw
- CanvasGroup中Alpha=0 不参与绘制,没有drawcall和overdraw,顶点不会参与重建,而SetActive会把他的顶点和材质弄成脏标记,会导致重建(Canvas 和 Canvas Group:https://www.jianshu.com/p/3a32e01a0bb1)
编译优化
Unity3D研究院新方法加快代码编译速度:https://www.xuanyusong.com/archives/4474
Unity的程序集Assembly与加快代码编译速度:https://blog.csdn.net/u012685888/article/details/121747831
内存优化
内存占用组成
- Unity(native堆,unity资源)
- 纹理Texture
- 大小
- 格式
- mipmap
- 压缩
- 图集
- 网格Mesh
- 顶点数
- 骨骼数
- 压缩
- 合批
- Animation
- 长度
- 关键帧数量
- 压缩
- Shader
- Font
- ......
- 纹理Texture
- Mono(写的C#代码,注意GC)
- GfxDriver(显卡驱动,渲染时使用的纹理材质Shader等)
- FMOD(Unity使用的音频引擎资源)
- Profiler(性能分析器本身也会占用内存)
- ......
内存GC
优化要点:
- 逐一定位,逐一分析
- 避免频繁的对象创建
- 尽量避免装箱拆箱
- 善用缓存
- 使用对象池
- 内存(逻辑对象)对象池
- 游戏物体对象池
关于GC,可以参考我的另外两篇文章:
- .NET CLR:垃圾回收和GC算法:https://www.lengyueling.cn/archives/98
- Unity:GC机制(Mono Runtime GC:https://www.lengyueling.cn/archives