Unity 几种优化建议
最简单的优化建议
- 控制顶点数:在 PC 平台,保持场景中显示的顶点数少于 200K - 3M;在移动设备上,顶点数应少于 10W。不过,具体顶点数需根据目标 GPU 与 CPU 的性能来确定。
- 选择高效 SHADER:若使用 U3D 自带的 SHADER,在表现效果可接受的情况下,优先选择 Mobile 或 Unlit 目录下的 SHADER,它们的效率更高。
- 共用材质:尽可能让不同物体共用材质,以减少资源消耗。
- 设置静态物体:将不需要移动的物体设为 Static,这样引擎可以对其进行批处理,提高渲染效率。
- 减少灯光使用:尽可能少用灯光,特别是动态灯光,因为灯光计算会增加 CPU 和 GPU 的负担。
- 采用压缩贴图:尝试使用压缩贴图格式,或者用 16 位贴图代替 32 位贴图,以降低内存占用和提高加载速度。
- 慎用雾效:如果游戏中不需要雾效(fog),则不要使用,以免增加额外的计算量。
- 使用遮挡剔除:尝试使用 Occlusion Culling,在房间过道多、遮挡物体多的场景中,该技术非常有用。但使用不当可能会增加 CPU 负担。
- 利用天空盒:使用天空盒来“褪去”远处的物体,减少不必要的渲染。
- 优化 SHADER 计算
- 在 SHADER 中,使用贴图混合的方式代替多重通道计算,以减少计算量。
- 注意 float/half/fixed 的使用,根据不同的计算需求选择合适的浮点类型。
- 避免在 SHADER 中使用复杂的计算,如 pow、sin、cos、tan、log 等。
- 尽量减少 SHADER 中的 Fragment 数量。
- 检查动画脚本:注意是否有多余的动画脚本,模型自动导入到 U3D 时可能会附带动画脚本,大量的动画脚本会严重影响 CPU 计算性能。
- 优化碰撞检测:注意碰撞体的碰撞层,舍去不必要的碰撞检测,以减少 CPU 计算量。
为什么需要针对 CPU 与 GPU 优化
CPU 和 GPU 都存在各自的计算和传输瓶颈,而且不同的 CPU 或 GPU 性能差异较大。因此,游戏需要针对目标用户的 CPU 与 GPU 能力进行开发,以确保游戏在不同设备上都能有良好的性能表现。
CPU 与 GPU 的限制
GPU 限制
GPU 通常具有填充率(Fillrate)和内存带宽(Memory Bandwidth)的限制。如果游戏在低质量表现下运行速度明显加快,那么很可能需要限制 GPU 的填充率。
CPU 限制
CPU 一般受需要渲染物体的个数限制,CPU 向 GPU 发送渲染物体命令称为 DrawCalls。通常情况下,DrawCalls 数量应尽可能控制,在能保证游戏效果的前提下,数量越少越好。一般来说,电脑平台上 DrawCalls 控制在几千个之内,移动平台上 DrawCalls 控制在几百个之内,但这并非绝对标准,仅供参考。
实际上,渲染(Rendering)本身可能并非性能瓶颈,问题很可能出在脚本代码的效率上。可以使用 Profiler 进行性能分析,关于 Profiler 的介绍可查看:Profiler
需要注意的是,在 GPU 中显示的 RenderTexture.SetActive() 占用率很高,这是因为同时打开了编辑窗口,并非 U3D 的 BUG。
关于顶点数量和顶点计算
CPU 和 GPU 都会对顶点进行大量的计算处理。GPU 中渲染的顶点数取决于 GPU 性能和 SHADER 的复杂程度。一般而言,每帧之内,PC 上的顶点数应控制在几百万以内,移动平台上不超过 10 万顶点。
CPU 中的计算主要集中在蒙皮骨骼计算、布料模拟、顶点动画、粒子模拟等方面;GPU 则主要进行各种顶点变换、光照、贴图混合等计算。
具体的顶点数还需根据项目需求来确定。例如,对于一款需要兼容低配置硬件、保证流畅运行并控制硬件发热,同时还要达到一定效果(如 LIGHTMAP + 雾效)的 3D 游戏,同屏 2W 顶点是比较合适的数目,DRAWCALL 最好低于 70。此外,为了控制发热,需要控制最高帧率,实际上,游戏流畅运行并不需要太高的帧率。
针对 CPU 的优化——减少 DRAW CALL 的数量
为了将物体渲染到显示器上,CPU 需要完成一系列工作,如区分需要渲染的物体、判断物体是否受光照影响、选择合适的 SHADER 并为其传参、向显示驱动发送绘图命令以及发送删除命令等。
假设用上千个三角形模型代替一个上千三角面的模型,虽然在 GPU 上的花费相近,但在 CPU 上的消耗会大幅增加。为了减少 CPU 的工作量,需要减少可见物的数目,具体方法如下:
- 合并相近模型:可以手动在模型编辑器中合并相近的模型,也可以使用 UNITY 的 Draw call 批处理(Draw call batching)达到相同效果。具体方法和注意事项可查看:Draw call batching
- 减少材质使用
- 在项目中尽量使用更少的材质,可将几个分开的贴图合成一个较大的图集。
- 如果需要通过脚本来控制单个材质属性,要注意改变 Renderer.material 会造成一份材质的拷贝。因此,应使用 Renderer.sharedMaterial 来保证材质的共享状态。
- 有一个合并模型材质的不错插件叫 Mesh Baker,可考虑尝试使用。
- 减少渲染步骤:尽量少用一些渲染步骤,如 reflections、shadows、per - pixel light 等。
- 确保材质共享:Draw call batching 合并物体时,每个合并后的物体至少应有几百个三角面。若合并的两个物体不共享材质,不会提升性能。为了提升 CPU 性能,应确保合并的物体使用同样的贴图。另外,使用灯光会取消(break)引擎的 DRAW CALL BATCH,具体原因可查看:Forward Rendering Path Details
- 使用剔除技术:使用相关剔除技术直接减少 Draw Call 数量,下文会有相关介绍。
优化几何模型
优化几何模型有两个基本准则:
- 避免不必要的三角面:模型中不应存在不必要的三角面,以减少顶点数和计算量。
- 减少 UV 贴图接缝和硬边:UV 贴图中的接缝和硬边越少越好,这有助于提高渲染质量和效率。
需要注意的是,图形硬件实际处理的顶点数与硬件报告的并不相同。模型处理应用通常展示的是几何顶点数量,而在显卡中,一些几何顶点会被分离(split)成两个或更多逻辑顶点用于渲染。如果存在法线、UV 坐标、顶点色等信息,顶点必然会被分离,因此游戏中实际处理的顶点数量会更多。
关于光照
在游戏中,不使用光的渲染速度最快。在移动端优化时,可以采用光照贴图(Lightmapping)来烘焙一个静态的贴图,以代替每次的光照计算。在 U3D 中,生成光照贴图所需的时间非常短。这种方法不仅能大大提高效率,还能获得更好的表现效果,如平滑过渡处理和附加阴影等。
在移动设备和低端电脑上,尽量不要在场景中使用真光,而应使用光照贴图。这样可以大大节省 CPU 和 GPU 的计算量,使 CPU 处理的 DRAWCALL 减少,GPU 需要处理的顶点和像素栅格化工作也相应减少。关于光照贴图的详细信息可查看:Lightmapping
对 GPU 的优化——图片压缩和多重纹理格式
图片压缩(Compressed Textures)
图片压缩可以降低图片大小,实现更快的加载速度和更小的内存占用。压缩贴图相比未压缩的 32 位 RGBA 贴图,占用的内存带宽要少得多。相关信息可查看:Compressed Textures
此外,曾有在 U3D 会议上提到,贴图尽量使用相同大小的格式(如 512 512、1024 1024),这样在内存中能得到更好的排序,减少内存空隙,但该说法尚未经过测试验证。
多重纹理格式(MIPMAPs)
多重纹理格式的原理与网页上的缩略图类似,在 3D 游戏中,为游戏的贴图生成多重纹理贴图,远处显示较小的物体使用小的贴图,显示较大的物体使用精细的贴图。这样可以更有效地减少传输给 GPU 的数据量。相关信息可查看:MIPMAPs
LOD、Per - Layer Cull Distances、Occlusion Culling
LOD(Level Of Detail)
LOD 是 3D 游戏中常用的技术,其功能类似于多重纹理贴图。它根据屏幕中显示模型大小的比例,判断使用高或低层次的模型,从而减少对 GPU 的数据传输和顶点计算。相关介绍可查看:Level Of Detail
摄像机分层距离剔除(Per - Layer Cull Distances)
为小物体标识层次,然后根据其距离主摄像机的距离判断是否需要显示,以此减少不必要的渲染。相关信息可查看:Per - Layer Cull Distances
遮挡剔除(Occlusion Culling)
当某个物体在摄像机前被另一个物体完全挡住时,不将其发送给 GPU 渲染,从而直接降低 DRAW CALL。但在某些情况下,在 CPU 中计算物体是否被挡住会消耗大量计算资源,可能得不偿失。相关介绍可查看:Occlusion Culling
关于 Realtime Shadows(实时阴影)
实时阴影技术效果很棒,但会消耗大量的计算资源,给 GPU 和 CPU 都带来较大的负担。详细信息可参考:Realtime Shadows
对 GPU 优化:采用高效的 SHADER
- 选择合适的 SHADER 版本:有些(built - in)Shader 有 mobile 版本,这些版本能大大提高顶点处理的性能,但可能会有一些限制。
- 避免复杂计算:自己编写 SHADER 时,要注意避免使用复杂的操作符计算,如 pow、exp、log、cos、sin、tan 等,每个像素点的计算中最多使用一次。不建议自己编写 normalize、dot、inversesqart 操作符,因为内置的操作符性能更好。
- 慎用 alpha test:alpha test 非常耗时,使用时需要谨慎。
- 选择合适的浮点类型:在浮点类型运算中,精度越低的浮点计算速度越快。在 CG/HLSL 中:
- float:32 位浮点格式,适合顶点变换运算,但计算速度较慢。
- half:16 位浮点格式,适合贴图和 UV 坐标计算,计算速度是 highp 类型的两倍。
- fixed:10 位浮点格式,适合颜色、光照等计算,计算速度是 highp 格式的四倍。
关于 SHADER 优化的小提示可查看:Shader 优化提示
另外的相关优化
- Draw Call Batching 优化:可参考 Draw Call Batching 优化文档 对 Draw Call Batching 进行优化。
- Rendering Statistics Window 说明:关于 Rendering Statistics Window 的说明和提示可查看:Rendering Statistics Window
- 角色模型优化:角色模型的优化建议包括使用单个蒙皮渲染、尽量少用材质、少用骨骼节点。在移动设备上,角色多边形数量保持在 300 - 1500 内(具体数量需根据实际需求确定);在 PC 平台上,角色多边形数量保持在 1500 - 4000 内(同样需根据实际需求确定)。详细信息可查看:角色模型优化文档