Appearance
性能优化参考
主场景资源限制
TIP
以下为主观测试数据,仅供参考
模块 | 维度 | Unity Engine | |
---|---|---|---|
模型 | 模型格式 | fbx/obj | |
面数上限(同屏) | 单模型 | 25W | |
总面数 | 125W | ||
模型尺寸 | 无要求 | ||
单文件模型上限 | 模型数量 | 30 | |
材质 | 类型 | 无要求 | |
材质贴图 | 尺寸限制 | 1024 x 1024 | |
文件格式 | png/jpg/jpeg/tga | ||
同屏材质数 | 135 | ||
动画 | 2D 序列帧动画 | 基础要求 | 可以自己用 shader 实现(可以用第三方插件) |
帧数限制 | 无 | ||
3D 动画 | 基础要求 | 无 | |
单模型骨骼上限 | 18 | ||
图片 | 图片 | 格式 | png/jpg/jpeg/tga |
最大尺寸 | 1024 x 1024 | ||
2D 素材压缩 | 是否有该功能 | - 编辑器导出时自动压缩处理(android:etc ios:pvrtc) - Unity 中可以修改 max size & compression 配置项-尽量使用不透明贴图 | |
多媒体 | 音频 | 格式 | mp3/wav |
大小 | 无 | ||
其他 | 无 | ||
视频 | 格式 | mp4/mov | |
大小(全景) | 视内存而定,最多 2G | ||
大小(α 通道) | 视内存而定,最多 1G | ||
大小(4K) | 视内存而定,最多 3G | ||
推荐分辨率 | 1080P 即可,往上性价比不高 | ||
灯光 | 类型 | 一盏平行光、烘焙点光源 | |
特效 | 粒子 | 同屏粒子 | 600 |
透明 | 同屏透明对象 | 25 | |
HDR | 同屏 HDR 对象 | 100 | |
物理 | 基础要求 | 详见 Unity 官方文档 | |
刚体数量 | 25 | ||
DrawCall | 极限上限 | 3000 后卡死 | |
抗锯齿 | 支持,4xMSAA 即可,过高性价比不高 | ||
效率 | 内存 | 最大限制 | <4G(内容资源占的内存,不包含算法) |
帧率 | 推荐高于 60,动作幅度高的内容推荐高于 72 | ||
包体 | 包体限制(仅渲染) | 最大 | 512M(内容资源,不包含算法) |
平台限制 | 导入限制 | ||
导出限制 | |||
编程语言 | C# |
资源优化
面数优化
- 在 Game 窗口的 Stats 渲染统计窗口选项可以查看各项性能开销数值. Tris 代表当前窗口渲染的三角面片数.
- 通过移动 maincamera 位置可以查看不同角度内容同屏三角面数,建议在运行状态下查看该数值.
- 素材模型三角面数过大时需要对模型面数进行优化,网上有很多教程和不同方法可以参考这里不做详细解释.
压缩贴图
- 在 assets 中找到贴图,点选后右侧 inspector 中会显示贴图各项数据.
- 将 MaxSize 设置为 1024 或以下尺寸,也可以选择 Compression 进行压缩. 选择完毕后点击 Apply 即可完成 Unity 内的贴图压缩. 此外也可以使用 https://tinypng.com 等工具进行贴图压缩.
GPU Instancing
- 为 Materials 启用 GPU Instancing:在 Project window 中选中你的 Material,然后在 Inspector 窗口中,勾选 Enable Instancing 复选框。
- 使用 GPU Instancing 可以一次渲染(render)相同网格的多个副本,仅使用少量 DrawCalls。在渲染诸如建筑、树木、草等在场景中重复出现的事物时,GPU Instancing 很有用。
- 每次 draw call,GPU Instancing 只渲染相同(identical )的网格,但是每个实例(instance)可以有不同的参数(例如,color 或 scale),以增加变化(variation),减少重复的出现。
- GPU Instancing 可以减少每个场景 draw calls 次数。这显著提升了渲染性能。
- Unity 只会在 Material 使用的 Shader 支持 GPU Instancing 时,才会显示这个复选框。这些支持 GPU Instancing 的 Shaders 包括:Standard, StandardSpecular 及所有的 surface Shaders。
CPU 优化
- 减少 draw calls
- 一般来说,在每一个 frame 的时间内应尽量控制您的 VR 应用不要超过以下限制:
- Triangle 或 Vertices:50000 到 100000 个。
- Draw calls:100 到 1000 个。
- 减少需要绘制的对象,包括 Renderers 及 Materials 的数量。
- 调整 Camera 的远距离裁切面来缩短需要绘制的距离,或是直接隐藏距离较远的物体。可以使用 霧化效果 让视觉上更自然,让使用者不会直接看见远方的物体忽然消失。
- Draw call 批处理 (Draw call batching)
- 动态批处理(Dynamic batching):对于数量众多的小型物体 (小于 900 个 vertex attributes,或 300 个 vertices) 请尽量使用相同材质,Unity 会自动使用动态批处理,减少 draw calls。
- 静态批处理(Static batching):对于从头到尾都不会移动旋转放大缩小的物体,请将它设为 static。Unity 会尝试将它们合并成一个大的 mesh 以减少 draw calls。(注意:这会增加内存使用量)
- 纹理图集 (Texture Atlasing)
- 将场景中使用到的 textures 合并成一张大的 texture。藉由这个做法,让许多对象共享同一个 texture,以便于批处理。
- 一般来说,在每一个 frame 的时间内应尽量控制您的 VR 应用不要超过以下限制:
- 如果使用 NGUI,您可能需要重新编排 UI 的阶层,以免必须要用到一个以上的 UIPanel。
- 物理运算
- 物理运算需要使用较高的 CPU 资源,大部分的情况下,并不是所有 layer 之间都需要处理物理运算。在 Edit > Project Settings > Physics 有一个各 layer 间交互作用的关系表,请依据实际需求,关闭不必要的 layer 间交互作用。
- Raycast 消耗不少运算,且性能与射线长度及场景中 collider 类型有关。建议使用“Layer Masks”过滤掉不需要的 layer,可以减少部分运算。
GPU****优化
使用“Mobile”版本的着色器取代“Standard”版本
- 您可以使用 Unity 中“Mobile”类别下的简化着色器(Shader)。这些着色器是专为移动装置所设计,能带来明显的性能提升。但要注意某些着色器只支持一个 directional light,而有些着色器并不支持 alphaTest 或是 ColorMask。
光源与打光
- 实时的照明与 reflection probes 耗用大量运算资源。建议尽量避免在移动式装置上使用。在许多情况下,使用预计算的 lightmap 及全局光照(GI)可以达到相似甚至可能更好的视觉效果,并因实时运算的减少而大幅降低所需的 GPU 运算资源。
- 打光时的阴影是另一个优化的重点。在 QualitySettings 中依照您的项目需求来调整 Shadow 的设定,特别是 Shadow Distance 。
其他建议与开发准则
- 请使用 Fixed Foveated Rendering (FFR) 来降低 GPU 使用率。FFR 即,由在外围区域降低显示分辨率改善 GPU 使用性能,并保持视野中央区域的显示分辨率不受影响。由于人眼的构造与生理特性,这样的作法并不会对视觉效果造成太大的负面影响。您可使用 Wave SDK 提供的 API 设置 FFR 的强度,依照实际对画质的影响与视觉感受来调整适合的设定值。
- 开启 Adaptive Quality (AQ) feature。AQ 开启时,系统将自动依据 CPU 与 GPU 的使用率,动态调整显示画质。依据系统实际使用情况,在运算率较高的时候,利用 Dynamic Resolution 动态降低显示分辨率,或是利用 Fixed Foveated Rendering 调降外围区域的画质。借此达到更佳的电源管理,更长的电池寿命,与更顺畅的刷新率(FPS)。
- 简化粒子系统(particle systems),为移动装置优化美术设计,简化 3D 网格使用较少的 vertices 与较小的 textures。
- 避免“重复绘制(overdraw)”。这是指同一个像素位置由于对象互相重迭,而被绘制了多次的情形。一个典型的例子是粒子特效(particle effect):若粒子特效使用的 texture 包含有大量带 alpha 透明度的像素,会需要耗用大量 GPU 运算资源处理碰撞侦测与重复绘制,要避免这种情况,应减少带有 alpha 透明度的像素数量。
Wave SDK设计准则与常见使用错误
- 不要在场景中加入多余的 main camera。某些情形下由于跨平台开发的原因,开发者在 VR 场景中加入 Unity 预设的 main camera,这会造成额外不必要的 GPU 运算,因为 WaveVR prefab 已经提供有内建的 main camera。多余的 main camera 即使对最后显示结果没有任何可见的作用,仍会造成额外运算负担。开发 WaveVR 应用时应确认场景中只有使用 WaveVR prefab 原生提供的 main camera,其他多余不应存在的都应移除或停用。
- 尽可能使用单通道渲染模式(Single pass mode)。VR 装置藉由每个 frame 分别为左右眼绘制显示影像以产生立体视觉,单次渲染能达到较好的性能。某些情况下,由于会发生显示异常而无法使用单通道渲染模式 (例如: 使用了不支持单通道渲染的着色器)。这种情况下请特别注意:不要在 Virtual Reality** Supported** 被勾选的情形下选用 Multi pass mode,您应该直接取消勾选 Virtual Reality Supported ,以免造成额外 GPU 运算。
为您的应用缩减档案大小
- 记得压缩您使用的 textures,既能够缩减最后 build 出来的档案大小,也能降低档案读取的时间。
- 将 Texture 压缩的图像译码工具设为 ASTC。这能藉由使用硬件译码而达到比 Unity 为 Android 预设的 ETC2 更好的性能。
Unity UGUI 小秘诀
- 在 UGUI canvas 底下改动任何组件时,Unity 会自动重新产生 UI 的网格,这是个有点耗时的动作。所以可以试着将 UI 分组成多个 canvas,让常常变动的 UI 影响仅限于小分组之内,减少网格重建所需的时间。
- 尽量减少使用 Layout system,建议使用 UIAnchor。
程序代码撰写
为常用属性建立缓存
- Unity 当中如果某些 Component 属性会经常被使用到,通常值得将它们缓存起来,而不要在每次需要使用的时候都呼叫 Unity API 来取用。举例来说,gameObject.transform, gameObject.renderer, Input.touches, Camera.main, Screen.width 等等,在 Unity 内部实际是通过 GetComponent() 或 FindWithTag()等具有搜索性能消耗的代码来执行,而这些 function call 的速度并不快。
避免过大的 GC 和内存 leak
- 在 coroutine 中最好不要使用“new WaitForSeconds()”,每一次的“new”都会用掉 20 bytes GC,您可以藉由 将“new WaitForSeconds()”移出 coroutine 之外 达到相同的效果,又可以避免这个问题。
- 不要频繁地创建或销毁物件。请设计一个 object pool,让创建的对象能够被重复使用。
- 使用 RaycastNonAlloc 来取代 RayCastAll ,新的 API 不会额外寻址一块内存。
改善性能与体验
- 考虑将游戏的主要逻辑运算移到另外的线程。Unity 是使用主线程,而且它的 API 并不是 thread safe,但您仍然可以开启另一个线程并把和 Unity 无关的游戏逻辑运算移到另外的线程。
- 加载复杂场景之前,可以先切换到一个空的或相对简单的场景,避免有两个很复杂的场景同时在内存中加载。
- SetActive() 会影响所有的 children 对象,这会消耗不少的时间。如果状况允许,可以把不需要的对象隐藏起来,移动到看不见或很远的地方。
- 不要使用 SetActive(false) 来关闭粒子特效。建议使用 ParticleSystem.Stop() (或较早版本的 Unity 中,将 emit 设成 0)。因为粒子系统底下通常会有很多 children,SetActive() 会作用在所有 children 对象一个一个关闭,所以比较耗时。
- 游戏中常常会同时播放多个音效,适当地预先加载避免因同一时间读取造成卡顿。
- 避免使用 foreach,尽量使用 for 循环来代替,执行性能较佳。
- 避免在 Update()或是每个 frame 都会呼叫的地方做 String 的运算。
- 使用 Dictionary 时,用原生型别当作 key,避免使用 string 或是其他复杂的型别 (复杂的型别使用 Equal()来进行 key 的比对,这个动作比较耗时)。
- Monobehavior 底下的 event function,例如 Start() , Update() 等等,如果是空的,请直接将它们移除。
- 如果可能,请使用 Array,避免 List 或是 Dictionary。
- 当 class 设计成不会再被继承时,请加上 Sealed,这样可以稍微增进性能。
其他改善性能的小秘诀
- Cast and Receive Shadows 在 Unity 默认是打开的,请考虑将它关闭。
- Skinned Mesh Renderer 在目前的移动式装置 GPU 上运算起来很耗性能,请尽量避免使用。
- 使用 animation culling:勾选这个选项,当挂有动画的对象不在可见范围时,停止或暂停播放动画。
- Unity 的外挂动画引擎,我们推荐使用 DOTween,在我们的经验当中它的资源耗用量以及执行时的性能都比其他的 tween engine 或 legacy animation 要好。
- 建议使用 UnityEngine.JsonUtility。 目前它是最快的 Json parser (但使用时请注意一下它的限制)
- 请尽量 使用 AssetBundle 来取代 Resource.Load, AssetBundle 对内存的使用较节省。
其他优化条例
- 使用三线性过滤或各向异性过滤来处理纹理。详见 Unity 手册中的“Textures”部分获取更多信息。
- 实现基于网格的遮挡剔除(请参阅 Unity 手册中的“Occlusion Culling”部分)。
- 始终使用前向渲染路径(详见 Unity 手册中的“Forward Rendering Path Details”)。
- 在 OVRManager 中启用“Use Recommended MSAA Levels”。要了解更多信息,请查阅“OVRManager”。
- 注意在 LOD 偏移后出现过高纹理分辨率(PC 上大于 4k x 4k,移动设备上大于 2k x 2k)。
- 确保非静态物体具有碰撞器,并在其本身或父级链中没有丢失刚体组件。
- 避免使用效率低下的特效,如 SSAO、运动模糊、全局雾和视差映射。
- 设置适当的物理设置,例如 Sleep Threshold 值大于 0.005,Default Contact Offset 值大于 0.0l,以及 Solver Iteration Count 设置小于 6。
- 减少多通道着色器的使用(例如传统的高光着色器)。
- 在启动场景中限制大纹理和预制件的使用,以加快启动速度。在可能的情况下对大纹理进行压缩。
- 避免实时全局光照。
- 在接近几何体或绘制调用限制时禁用阴影。
- 注意像素光的数量(不超过 2 个)和渲染比例(不超过 1.2),以避免过度的 GPU 负载。
- 限制着色器通道的数量,避免性能瓶颈(不超过 2 个)。
- 在处理大文件下载时,谨慎使用 Unity 的 WWW 功能。对于非常小的文件,使用该功能是可以接受的。
- 对于具有语音聊天的 Android 应用程序,请使用 Microphone API 以避免与 Parties 相关的问题。
- 对于大型场景同屏三角面数不超过 50w 面