目录
https://blog.csdn.net/poem_qianmo/article/details/78309500
一、前言
性能优化是游戏开发绕不开的重要环节。可以提升用户体验、降低设备能耗、扩大受众市场,将更好的游戏画面展示给玩家。
本篇文章是对工作中常用的性能测试知识点做个总结。
二、测试基础
1.用例设计
理解需求后进行用例设计,避免漏测至线上
以下介绍常用的测试用例设计方法,万变不离其宗,在设计用例前可以通过和几个方法寻找思路,避免漏测
这些方法就是为了在设计用例的时候,用最简单的方法覆盖最全的测试内容
测试方法 | 原理 | 应用 | 使用场景举例 |
等价类划分 | 将输入数据划分为有效和无效的等价类,每个等价类的数据应当被软件以相同的方式处理 | 为每个等价类选择至少一个代表性的值作为测试用例,以减少测试用例的总数且尽可能全面地覆盖所有可能情况。 | 活动关卡数量较多时,可以根据角色、场景、关卡类型等特点进行拆分,大致相同的关卡可以选择性测试 |
边界值分析 | 错误往往发生在输入或输出范围的边界上。 | 不仅测试每个等价类的代表值,也要测试边界值。 | 测试关卡时,可以测试场景最复杂、怪物数量最多、女武神组合最复杂时的情况 |
因果图法 | 通过分析输入(原因)和输出(效果)之间的逻辑关系,用图的方式表示并转换为决策表,从而生成测试用例。 | 适用于逻辑条件复杂的场景,如多个条件组合导致不同的结果。 | 例如测试渲染feature时,将不同的画质选项分别组合成,可能有异常效果 |
判定表 | 对于每个业务逻辑,列出所有可能的条件组合及其对应的动作,将复杂的业务规则结构化表示。 | 有效处理那些具有多个条件和行动的复杂决策逻辑。 | 枚举所有可能出现的结果,例如4个角色,4个关卡,可能有16种角色进入方式 |
状态转换测试 | 基于系统的状态及状态间的转变来设计测试用例。 | 适用于系统的状态较多,且状态间转换复杂的情况。 | 例如第一部舰桥和零号世界舰桥时进入零号世界时的表现,第一部舰桥进入耗时会增加 |
使用(用例)场景测试 | 根据系统的使用场景来设计测试用例。 | 模拟实际软件的使用方式,适用于冒烟测试、验收测试等。 | 平常测试时用压缩包来解压PC包,模拟线上情况应该用启动器下载PC包 |
错误推测 | 基于经验来猜测软件中可能存在的错误,设计测试用例。 | 可以补充其他方法遗漏的测试用例,特别适用于经验丰富的测试人员。 | 当发现线上反馈什么资源不显示时,先尝试切到流畅画质或使用低端机试一下 |
探索式测试 | 在没有或者只有极少量测试文档的支持下,依靠测试者的直觉和经验进行测试。 | 通常用于快速发现问题的情况,如迭代开发初期或新功能的初步测试。 | 冒烟一个demo的时候,可以利用经验,快速的将外围、玩法冒烟一遍,同时可以判断此需求后续的性能风险等级 |
组合测试 | 通过系统地组合输入参数的不同取值,用于发现不同输入组合下可能导致的问题。 | 适用于输入参数较多,测试全排列不现实的场景。 | 测试聊天框时,可以尝试sql注入,富文本等输入方式,查看是否有问题 |
配对测试 | 基于绝大多数软件缺陷都是由单个或者一对参数引起的假设,测试所有可能的唯一参数对。 | 一种有效率的组合测试手法,可以大幅减少测试的总数和成本。 | 测试AA时,可以开启TAA测试一遍不同画质的耦合情况,再开启FXAA测试一遍,其实就是单元测试搭配耦合测试的一种 |
正交阵列测试 | 使用正交阵列定义测试用例,以便在最少的测试用例中测试到最多的参数组合。 | 适用于需要测试多个变量的场景,尤其是变量间有交互时。 | 例如某活动场景1+场景2,角色A+角色B,可以测试场景1+角色A,场景2+角色B,在比较忙的时候可以这样测试减少压力,但一定要做到心中有数 |
2.性能指标含义
FPS (Frames Per Second)
定义: FPS 表示屏幕每秒能够刷新的帧数。这是衡量视频游戏或图形应用程序平滑程度的重要指标。
影响用户体验: 较高的 FPS 意味着更加流畅的画面和更好的用户体验。对于大多数游戏,60FPS 被认为是良好的用户体验的基线,手游中则至少要在22帧以上,而对于专业级的玩家,他们可能更倾向于更高的帧率,比如120Hz或144Hz及以上。
计算方式:游戏帧率从计算方式上来看分为逻辑 FPS 和硬件刷新 FPS,一般情况下游戏引擎工具统计的帧率都是逻辑帧率,即把每次调用底层 SwapBuffer 之间的间隔当做每帧耗时,然后根据耗时计算出逻辑 FPS。逻辑 FPS 实际上和玩家看到的画面刷新帧率会有区别,一般移动设备的屏幕刷新频率是 60HZ,即每秒刷新 60 次,VSync(垂直同步) 时间为 16.6ms,真实的画面刷新耗时是 16.6ms 的倍数。
Frametime
定义: Frametime 是渲染一帧图像所需的时间,通常以毫秒为单位。它与 FPS 有直接的关系,即 Frametime 为1秒钟的时间除以 FPS 值。
影响用户体验: 低而且稳定的 frametime 表示图像流畅无延迟。如果 frametime 高或者波动大,即使 FPS 总体平均值较高,玩家也可能感受到卡顿和不流畅的画面。
Jank
定义: Jank 指的是由于渲染延迟或者性能不佳导致的屏幕卡顿或者跳帧现象。
影响用户体验: Jank 会严重影响用户体验,因为它破坏了画面流畅性,让动画或移动显得不自然、突兀。
Draw Call
定义: Draw Call 是指 CPU告诉GPU绘制图形对象的指令。每次 draw call 可以包括数万至数百万的图形多边形(通常是三角形)。
影响性能: Draw Calls 的数量可以显著影响性能,因为每个 draw call 都需要处理时间。过多的 draw calls 会减慢渲染过程,降低 FPS,建议drawcall峰值不要超过500。
Tri (Triangles)
定义: 在3D图形中,"Tri" 指的是三角形,它是构建3D模型和场景的基本单位。GPU的工作是根据这些三角形来渲染图像。
影响性能: 三角形的数量可以直接影响渲染性能。更多的三角形意味着复杂的模型和场景,但也增加了GPU的工作负担。建议Tri峰值不要超过50万。
Memory (RAM/VRAM)
定义: Memory指的是计算机RAM(随机存取存储器)或是专门为GPU设计的VRAM(视频随机存取存储器)。它们被用来临时存储数据,如纹理、顶点数据等。
影响性能: 足够的内存可以确保快速存取和处理大量数据。内存不足可能导致性能瓶颈或闪退,因为系统或GPU必须使用更慢的存储设备,如硬盘或固态硬盘。
Transparent Screen Ratio
定义: 半透明屏占比
影响性能: 直接影响FPS和功耗,轻则导致发热过快,重则导致明显的掉帧,建议峰值不要超过标准值:8层
3.基础指标
以某二次元RPG游戏为例,iOS中档性能标准。
fps | 28 |
frame_time | 40 (ms) |
jank | 3 |
jank_time | 416 (ms) |
mem_app | 1650 (MB) |
mem_mono_heap | 160 (MB) |
mem_dynamic_vbo | 20 (MB) |
gfx_dc | 500 |
gfx_tri | 500000 |
gfx_batches | 500 |
gfx_transparent_screen_ratio | 800 (%) |
gfx_light_screen_ratio | 200 (%) |
gfx_light_num | 15 |
gfx_water_screen_ratio | 100 (%) |
gfx_effect_screen_ratio | 800 (%) |
gfx_volumetric_screen_ratio | 100 (%) |
budget_npc | 15 |
budget_gadget | 40 |
budget_monster | 18 |
budget_monster_battle | 18 |
count_screen_effect | 2 |
count_grab | 1 |
count_all_preload_monsters | 25 |
功耗
total | 1150 (mA) |
三.卡顿问题定位
如果遇到突然卡顿,多半是cpu问题,可以使用profiler查看详细信息;如果是持续低帧,多半是gpu问题,可以使用xcode查看渲染耗时。
1. 性能截帧测试工具集合
工具 | 特点 | 描述 |
简单、易用、功能强大、推荐⭐ | 定位性能与渲染bug必备,第一步用它 | |
需要接入sdk | 功能强大,需要接入sdk,暂时没用上 | |
高通专用 | 高通设备推荐使用,可看各线程状态 | |
iOS专用,功能强大⭐ | iOS调试必备,unity profiler不行的话用它 | |
安卓与pc,截帧⭐ | PC与安卓调试必备,unity profiler不行的话用它 | |
查看线程状态 | 使用较少 | |
跟踪CPU和GPU的事件,截帧 | 目前支持的设备较少 | |
性能追踪、系统分析 | ||
对于高通的机器支持较好,可视化也较为方便。 | ||
需要机器得到Root才可以进行profiler | ||
查看CPU,内存指标较为方便 | ||
高通推出的 Android 应用性能和功耗测试工具 | ||
Google 官方推出的 Android 应用电量分析工具 | ||
密歇根大学推出的 Android 设备功耗分析工具 | ||
硬件功耗测试仪器 |
2.包含数据
指标 | QM平台 | Xcode | instruments | perfdog | SnapDragon Profiler | Unity Profiler | RenderDoc | systrace | |
常规数据 | CPU占有率 | √ | √ | √ | √ | √ | |||
内存 | √ | √ | √ | √ | √ | √ | |||
FPS | √ | √ | √ | √ | √ | √ | |||
GPU占有率 | √ | √ | √ | √ | √ | ||||
耗电量 | √ | √ | √ | √ | |||||
温度 | √ | √ | |||||||
帧耗时 | √ | √ | √ | √ | √ | √ | |||
DrawCall | √ | √ | √ | √ | √ | ||||
IO | √ | √ | √ | ||||||
网络流量 | √ | √ | √ | √ | |||||
CPU核心频率 | √ | √ | √ | √ | √ | ||||
渲染相关 | OverDraw | √ | √ | √ | √ | √ | √ | √ | |
面数 | √ | √ | √ | √ | √ | ||||
帧渲染耗时 | √ | √ | √ | √ | √ | ||||
函数耗时 | √ | √ | √ | ||||||
贴图分辨率 | √ | √ | √ | √ | √ | ||||
资源内存占用 | √ | √ | √ | √ | √ | √ | |||
线程耗时 | √ | √ | √ |
3.常见情形
常见问题 | 定位思路 | |
性能问题 | dc与面数超标 | 使用framedebug或hierarchy窗口隐藏物体,查看性能窗口或Stats中的性能数据,定位性能较差位置 |
卡顿 | 使用unity profiler查看帧耗时,ios端可以使用xcode查看gpu耗时 | |
渲染问题 | 三端通用问题 | 使用unity直接修 |
安卓端独有问题 | 先使用framedebug,再使用Renderdoc | |
PC端独有问题 | 先使用framedebug,再使用Renderdoc | |
ios端独有问题 | 先使用framedebug,再使用Xcode |
4.CPU时间片预算
4.GPU
5.资源角度
5.1特效性能标准指南
粒子系统个数 | 一个特效prefab包含的particle system的个数 | |
粒子系统组件检查 | 没有勾选Renderer,或勾选了Renderer却没有给Material赋值的 或没有勾选Emission的都认为是不符合规范的粒子系统 | |
粒子生命周期 | 检查Particle System下Emission栏中的Rate over Time是否为0, 且Looping开关为关闭状态。 若MaxTime + 1.0s > Duration 或者 Duration < MaxTime ,则认为不合规范。 并且在表格中给出报错:「Duration设置过大,建议值为 MaxTime + 0.5s」 取Bursts列表中Time最久的粒子爆发时间作为MaxTime | |
未激活节点检查 | 不允许存在未激活的物体 | |
特效资源引用路径检查 | ||
Texture大小 | 保证PC,iOS和android都勾选,且贴图max size都设置为 <= 512 | |
Anim动画大小 | ||
AnimController动画大小 | ||
Render Mesh面数 | Mesh tris * max particles 图中就是300 * 50 = 15000 且是单特效下,所有的mesh render累加的值 | |
Shape Mesh面数 | 这个的shape mesh就是232 | |
特效依赖检查 | ||
Cupe Map引用检查 | 特效引用的cubemap pc的max size < 1024 iod和android的max size < 256 | |
特效引用material是否丢失shader | missing标记 | |
特效有aniamtor和animation组件的,clip是否丢失 | missing标记 同上 | |
mesh particle的Mesh是否丢失 | missing标记 同上 | |
特效引用material是否为空 | missing标记 同上 | |
特效有aniamtor和animation组件的,clip是否为空 | missing标记 同上 | |
Sub Emitters | 禁止为空 | |
特效的layer是Effect | particle system的layer禁止为Effect以外的 特殊场景特效需求加白名单 | |
cast shadow, receive shadow, receive decals都设置成Off |
Collision模块 | 禁止使用 | |
Light模块 | 禁止使用 | |
External Force模块 | 禁止使用 | |
空发射器检测 | 发射为0或者不勾选 | |
UV3 UV4检测 | 禁止uv3 uv4 | |
UV2检测 | 因CustomData存在,可以使用 | |
Color检测 | 同上 | 因CustomData存在,可以使用 |
Trail Material | 禁止勾选Trails但是Trail Material为空或missing | |
Scene_Base shader | particle system禁止使用Scene_Base shader | |
Character和Prop shader | 同上 Character和Prop文件夹下的shader 都禁止使用 | |
特效层级检查 | 禁止出现3层以上的prefab层级 | 有ParticleSystem的算1级 |
max paricles >= 特定值,需要用EmiLevel优化 | ||
Motion Vectors 设为 Camera Motion Only | ||
reflection probe关闭 | ||
Light Probes关闭 | ||
StartLifeTime < 999 | ||
禁止挂MonoLightProbManager | ||
尽量不要用3D Noise噪声 | ||
Max Overdraw | 总绘制次数/绘制像素数 | 只有被画的像素才会进入分母统计 数据会更高,比较适合高频特效/小面片的特效 |
Max Screen Ratio | 总绘制次数/屏幕像素数 | 屏占比概念 |
5.2特效性能标准
四.性能问题定位流程
1.画面不流畅
问题描述:游戏运行时流畅度不足。FPS稳定但是明显低于目标值,如目标帧率60帧,FPS长期在50帧上下浮动
首先确定是CPU消耗过高还是GPU消耗过高,通过CPU单帧耗时和GPU单帧耗时来衡量
定位目标 | 指标 | 指标说明 | 指标异常情况 | 可能原因 | 获取工具 |
CPU消耗过高导致不流畅 | CPU单帧耗时 | CPU单帧耗时 | 数值高(大于16.6ms/33.3ms): 说明当前帧游戏无法达到 60/30 fps, 且CPU是性能瓶颈之 | 脚本函数、引擎函数耗时高 DP高,CPU要花费大量时间执行贴图绑定、shader切换、渲染状态设置等准备工作 | 引擎接口 |
GPU消耗过高导致不流畅 | GPU单帧耗时 | GPU单帧耗时 | 数值高(大于16.6ms/33.3ms): 说明当前帧游戏无法达到 60/30 fps, 且GPU是性能瓶颈之一 | 场景复杂(面数高、贴图精度高、shader 复杂) Overdraw严重 | 引擎接口 |
1.1 CPU单帧耗时高
定位目标 | 指标 | 指标说明 | 指标异常情况 | 可能原因 | 获取工具 |
DrawCall过高 | DrawCall | 一帧中,CPU向GPU提交的DrawCall数量 | 数值高: 导致CPU单帧耗时高,可参考游戏DrawCall标准来衡量是否超出规定的DrawCall数 (iPhone 6S上DP达到800会产生明显卡顿) | 场景中提交渲染的物体数量多 场景中物体的材质区分不合理 启用了硬件遮挡剔除 遮挡剔除失效,不可见物体提交渲染 | 游戏内置GM工具-PerformanceTools |
RenderThread耗时过高 | RenderThread单帧耗时 | 一帧内,RenderThread线程运行时长 | 数值高:CPU花费大量的时间在渲染数据的准备上 | DP数高,可参考DP数 场景复杂(面数高,贴图精度高,物体数量多) | 安卓:systrace |
GameThread耗时过高 | GameThread单帧耗时 | 一帧内,GameThread线程运行时长 | 数值高:CPU花费大量的时间在游戏逻辑的处理上 | 游戏脚本耗时高 引擎函数调用耗时高 | 安卓:systrace |
函数耗时过高 | 函数耗时 | 函数调用关系,耗时分布 | 数值高:CPU函数调用耗时高 | 函数涉及到IO操作 函数算法实现复杂,耗时高 函数被高频调用(tick,AOI) | unity profile |
1.2GPU单帧耗时高
定位目标 | 指标 | 指标说明 | 指标异常情况 | 可能原因 | 获取工具 |
面数过高 | 面数 | 一帧中,提交渲染的物体总面数 | 数值高: | 场景、组件、角色模型等面数过高 | 游戏内置GM工具-PerformanceTools |
某个渲染环节耗时过高 | 单帧Encoder耗时 | 一帧内,各个渲染环节的耗时 | 数值高: 该环节渲染耗时高,需要进行优化 | 定位问题环节,具体分析:阴影渲染环节、不透明渲染环节、半透物体渲染环节、后处理环节、UI渲染环节等 | iOS:Xcode 安卓:Renderdoc |
算术逻辑环节操作耗时过高 | ALU Limter | 一帧内,GPU花费在处理算术逻辑上的时间 | 数值高: GPU耗费大量时间在算术逻辑操作上 | Shader代码复杂,耗时高: 使用大量的高精度浮点数 存在大量高低精度互相转换操作 存在复杂运算(sin,dot,sqrt,recip等) | iOS:Instruments 安卓:SnapDragon Profiler |
overdraw较高 | overdraw | 一帧内,平均一个像素绘制了几次 | 数值高: CPU耗费大量的时间在片段着色上 | 存在一定数量的AlphaTest物体,或者半透物体提交渲染,导致HSR中断,buffer中的像素必须提前执行Flush操作 PreZ没有很好的生效,同时遮挡剔除效率低 场景中存在大量小面片或细长面片 | iOS:Xcode 安卓:SnapDragon Profiler editor:overdraw检测工具 |
2.游戏卡顿
问题描述:游戏运行时发生的卡顿情况
定位目标 | 指标 | 指标说明 | 性能异常情况 | 可能原因 | 获取工具 |
触发有问题的函数或资源、关卡加载导致卡顿 | 函数耗时 | 函数调用关系,耗时分布 | 数值高: CPU函数调用耗时高 | 触发存在问题的函数 特效、怪物等资源加载导致的卡顿 | unity profile |
3.游戏闪退
问题描述:游戏运行时发生闪退
定位目标 | 指标 | 指标说明 | 指标异常情况 | 可能原因 | 获取工具 |
资源占用内存过高 | 资源对应的内存大小 | 不同类型资源的占用情况 | 数值高: 某类资源占用高或者某一时刻加载资源过多 | 贴图、模型、碰撞模型、UI等资源占用内存高 | APM查询闪退log iOS可查看系统日志 安卓连接catlog再次复现闪退,录取闪退日志 游戏内置GM面板PerformanceTools查看闪退前内存峰值是否接近闪退阈值 |
内存泄漏 | 内存变化趋势 | 一段时间内游戏进程占用内存变化情况 | 内存持续升高 | 没有及时释放不再引用的资源 资源重复加载 代码引用对象没有正常回收 | APM 查询闪退log |
游戏开发中造成的闪退 | —— | —— | —— | 资源文件冲突或功能bug等 | APM 查询闪退log |
4.发热严重
问题描述:游戏运行时发热严重
定位目标 | 指标 | 指标说明 | 指标异常情况 | 可能原因 | 获取工具 |
CPU/GPU长期处于高负荷状态 | CPU/GPU占用率 | CPU/GPU使用情况 | CPU/GPU占用率长期处于高位 | CPU负载过高 GPU负载过高 | perfdog |
五.常用优化方法
1.导读
① 性能优化是一个长期的过程
② 性能优化不但是程序部门的事情,而是需整个项目组共同配合和努力的结果,一般项目组每个部门都会有对应的专职人员
③ 好的优化工具至关重要
④ 不同的机器设备,展示的美术品质是不一样的,所以针对机型(分三档机器)有不同的优化策略
⑤ 每个核都充分利用,多线程渲染技术(unity5 以上就开始支持了)
⑥ 在不同的场景,时间和空间的选择(时间换空间,还是空间换时间,CPU 和 GPU 可以互换,CPU 和内存可以互换、内存和磁盘可以互换)
⑦ 不必要渲染的东西不渲染(比如 UI 层级,不显示的 UI 不渲染;不在摄像机内的不渲染;遮挡减少渲染;imposter,LOD,minimap 等)。不必加载的内容不加载(比如大表拆分按需加载;图集拆分按需加载等;AB 包加载策略等)。能压缩的都压缩(这个好理解)
⑧ 性能优化不是一劳永逸的事情,是持久战;即便是已经上线多年的长线产品,性能测试也是一如既往在挖掘性能点并优化
参考:https://segmentfault.com/a/1190000019844821?utm_source=tag-newest
2.场景优化
优化方式:
1.贴图合并复用,核心目的为减少DC,提高贴图纹素利用率
2.简单的变更颜色使用材质参数控制而不是直接换色贴图
3.模型拆分复用,按照拆出部件材质减少主体建筑材质数为目的拆解部件拆解部件最好单独材质,调整简化冗余设计细节,大致还原原画感觉。
4.控制设计建筑设计出差异感会显得场景有层次变化,但会导致贴图复用度下降,有更大的贴图内存压力(共用细节多建筑风格更统一、性能好,但同一区域缺乏变化容易枯燥)
5.简化细节、整合雷同的结构设计,尤其是玩家不常接触与关注的位置
6.保持设计的大方向和建筑语言,减少玩家不常接触区域的复杂造型和结构,保证活跃区域的制作精度与质量
7.曲线结构多也增加了顶点的消耗
8.LOD制作方式会让场景始终占用内存,导致场景内存占用过高,但相应的整体场景美术效果会好于Streaming制作方式。Streaing制作方式会动态的加载和卸载场景资源,达到节省内存的效果,但美术效果也会差一些。
9.添加关键遮挡物,来减少相机内的渲染量。
3.同屏人数优化
① 同屏人主要集中的场景点,第一是要场景分离,比如拍卖行和交易地点分开,一个室内,一个室外;第二是减少对应场景的物件消耗
② 计算策略优化,优先渲染离自己近的角色和场景物件
② 效果优化,比如同一类型的特效到一定数量后不在渲染等
④ 同屏时用户角色是动态的,不停的加载和释放,做平滑处理(按帧控制渲染数目方法等)。避免瞬间加载导致的卡顿
⑤ 同屏战斗时,尤其国战,同角色 avatar 一样
⑥ Imposter(伪装者,或者叫替代物)技术:(基本原理用相机把模型各个角度的图片拍下来,然后根据玩家相机的角度选取不同的图片显示) 等等(方法还有很多)
4.UI 优化
比如:
① 格式
② 尽可能将动态 UI 元素和静态 UI 元素分离到不同的 UIPanel 中(UI 的重建以 UIPanel 为单位),从而尽可能将因为变动的 UI 元素引起的重构控制在较小的范围内
③ 带通道和不带通道图集,尽量分开
④ 同一个 UI 界面或者同一个功能点的 ui 资源放在一个图集,可以减少 drawcall 等等(方法还有很多)
参考:如何快速优化手游性能问题?从UGUI优化说起 - 腾讯WeTest - 博客园
5.GC 优化
GC 的时候会占用大量计算资源,如果 GC 之后发现内存依然不够用,需要再次分配(unity 非官方数据一次大概 6MB),那么就更耗资源了。GC 优化一般有:
① 对象池技术---重复使用对象(比如子弹,需要频繁的生成和销毁)
② 减少内存垃圾的数量(比如:不要在频繁调用的函数中反复进行堆内存分配,后者使用缓存技术)
③ 减缓 GC 的时间,不要在关键时候 GC 操作;在非敏感期主动 gc
④ 其他技术手段,比如装箱,list,字符串等导致 GC 的解决方案 等等(方法还有很多)
6.内存优化
内存性能原因主要有这几点:内存碎片过多(内存池解决)、内存频繁创建销毁(对象池解决)、内存加载慢、内存占用过高。
① 资源(贴图,模型,动画,声音等)有损压缩,每种资源都有最优的压缩方式和格式等。
② 脚本和配置,及时卸载,以及拆分等
③ 第三方 shader 库,冗余预编译宏占用
④AB 包打包策略和加载策略,都会影响内存占用。AB 自动打包工具(公共包合并,AB 包要在 10MB 内),游戏类型逻辑强关联打包模式,比如 moba,把一个英雄的所有资源打到一个包里。比如:第三方库内存占用,延时加载和模块隔离方法(第三方库内存泄漏,或者占用过大,方便处理) 比如技能表,
常驻优化方案 去掉内存中同时存在的相同资源(比如特效资源)
Unity 资源内存优化,资源内存占用排行榜(优化性价比):贴图>动画>网格>音频>材质
一、贴图优化
1.降低分辨率
2.拆分透明通道
3.调整压缩格式(比如带阿尔法通道的贴图拆分 2 两张,分别压缩;格式转换大概是原来 1/4 的内存)
4.禁用 Mipmap
5.启用 Use Crunch Compression
1 张 512*512 位图,安卓:RGBA ETC2(带通道)占用 256KB,RGB ETC2(不带通道)占用 128KB 内存;在 iOS:RGBA PVRTC(带通道)和 RGB PVRTC 都占用 128KB 内存
二、动画优化
1.减小动画长度
2.减少骨骼数量
3.减少关键帧密度
4.减少动画精度
三、网格优化
1.减少顶点
2.开启 Optimize Mesh 选项
四、音频优化
建议较长的音频使用.mp3 或.ogg 格式,较短的音频使用.wav 或.aiff 格式
7.CPU 优化
方法:
微观方法:从每一帧中发现问题(主要方法)
宏观方法:从统计中发现问题,比如一局战斗,每个模块和函数的耗时和调用次数统计,也方便不同版本的对比(工具或者日志来统计)
分类:
①GC,GCAlloc 耗时
② 渲染耗时(合并批处理,减少 drawcall:比如使用动、静态批处理,GPU Instance 技术)
③ 逻辑耗时(比如循环查找,比如大量服务器消息集中处理等)
④ 其他引擎消耗(动画,物理)
处理方式:
(1)关注游戏内热点函数,对热点函数重点优化
(2)降低游戏逻辑复杂度,精简算法,关注每帧需要执行的逻辑函数,减少复杂的数学运算
(3)合理控制组件的访问频率,对于物理和碰撞检测注意合理应用相关系统类的方法
(4)适当考虑预加载及内存池的利用,提高资源访问效率
注:iOS CPU长时间过载会被强杀,如90%持续60s被强杀等
8.GPU 优化
GPU 的计算目前看,大部分还是能胜任,设置错错有余的。所有在 CPU 优化方案里,有一部分就是把 CPU 做的事情,让 GPU 做。
GPU 优化思路:
① 减少渲染批次
② 同一个 drawcall 里,减少渲染类型
③ 平滑渲染
9.细节点
① 模型尽量不要做成多个物体挂点组合的方式,如果多个物体用到不一样的材质,那额外产生的 DC 可是很酸爽的
② 如果模型是带动作的话,模型的顶点数和骨骼数也是模型动作制作时的重要标准。
③ 图片占用内存的大小与图片大小、颜色深度、图片格式有关,与美术是否对图片资源进行压缩无关(android 用 ETC1,ios 用 pvrtc,不带 alpha 通道图片可以用 jpg)
10.性能监控
① 内部监控(上传资源时性能监控,和版本测试时,性能监控;从而及时发现问题)
② 外网性能监控,比如 wetest APM 监控系统。
12.耗电相关
工具:比如 google 的 Battery Historian 优化思路:
① 计算优化。算法、for 循环优化、Switch..case 替代 if..else、避开浮点运算
② 避免 Wake Lock 使用不当
③ 使用 Job Scheduler 管理后台任务
④ 降低亮度等
13.卡顿
处理方式:
(1)分析瓶颈环节,针对性对问题进行进一步分析
A. CPU负载过高引起,关注热点函数并进行相关优化
B. 渲染瓶颈引起,注意关注渲染资源的复杂度,如:面数过高、纹理精度高等;
(2)相关重负载的线程在大小核上的运行是否合理
(3)LOD的设置是否符合预期
六.兼容性问题和预想结果
类型 | 描述 |
硬件 | PC、iOS、Android、模拟器不同系统、内存下、厂商游戏运行的表现; 尽量在达到运行标准、不同性能的设备上能流畅或高程度运行 |
安装 | 覆盖安装无sdk异常、空间不足等问题,安装成功; 卸载重装正常,能安装成功 |
启动 | 启动无闪退、报错、黑屏等问题; patch能正常下载完成、速度正常 |
运行 | 运行中无闪退、无响应问题,运行稳定; 各性能指标达到预想标准 |
功能 | 核心玩法能正常体验,功能玩法无trace或bug |
UI、适配 | UI背景在不同分辨率下能覆盖全场景,无重叠等; 不同类型的屏幕下,无按钮或界面遮挡 |
特效、模型及场景 | 在不同性能的设备上特效能按分级正常播放,无卡顿; 不同状态及环境下,模型正常; 夜晚场景在设备最暗亮度下不影响体验,阴影正常,在不同视角下看场景正常; 渲染方案显示正确,如安卓高端机用Vulkan,中低端机用openGL |
音频 | 音频资源能完整播放,无卡顿,和画面同步,音质无损 |
流畅度 | 肉眼感觉不到卡顿,超过90%时间的帧率在锁帧(30fps)及以上 |
系统操作 | 锁屏唤醒后游戏能正常体验、切进程不会被杀死,后台音效不能继续播放等; 手机深色模式下,画面表现力; 浏览器或其它视频软件小窗播放视频、社交APP视频、播放音乐情况下游戏运行效果 |
发热 | 温手/烫手/非常烫手/非非非非非非常烫手 |
问题策略参考
1. 保证服务器、客户端及patch稳定
2. 确定操作条件、流程操作、GM指令正确
3. 遇到异常时,排查方法,保留现场或截频、录屏,发送给对应负责人解决
4. 不在标准外、不影响体验的问题可忽略
七.内存详细数据类型
在性能测试中经常遇到内存增长的问题,为了了解内存增长后比增长前多了哪些资源导致的内存增长,经常会用到profiler中的“内存精细化数据”进行内存打点,查看异常增加的资源,来进行对应的内存优化。
Type Name | 简述 | 拓展链接 |
---|---|---|
AK开头 | 音频 | |
Animation | 动画 | |
AnimationClip | 动画片段(Animation Clip) 是 Unity 中最小的动画构造块。它们表示一段独立的运动(如“向左奔跑”(RunLeft)、“跳跃”(Jump) 或“爬行”(Crawl)),可以采用各种方式进行处理和组合,以生成生动的最终结果 | |
Animator | unity组件,用来完成游戏对象的动态效果的控制和创建 | |
AnimatorController | 动画控制器,unity中的一个组件,可以查看和设置角色行为 | |
AnimatorOverrideController | Animator Override Controller是用来配合Animator Controller使用的,它让Animator Controller变得更加实用,可以让不同的使用实例的在同一状态播放不同的动作,但保留原有的结构、参数和逻辑 | 动画系统-Animator详解 - 简书 |
AssetBundle | 是从unity项目中打包出来的资源文件。AssetBundle文件,也叫AB包,可以存储任何一种Unity可以识别的资源,如模型、纹理图、音频、场景等资源,也可加载开发者自定义的二进制文件。AssetBundle支持3种格式的压缩,分别是LZMA、LZ4、无压缩,默认是LZMA格式的压缩,使资源文件体积减小,便于下载和传播 | |
AudioManager | 音频管理器 | |
Avatar | 女武神 | |
Battle hub | 战斗中心 | |
BehaviorDesigner开头的 | unity插件行为树 | Unity实用插件Behavior Designer - 哔哩哔哩 |
BoxCollider开头的 | unity组件,碰撞盒相关 | |
BuildSettings | unity组件,构建设置 | Build Settings - Unity 手册 |
Camera | 相机 | |
Canvas | 画布渲染器,主要目的是为模组创作者提供更多控制和选项,以帮助他们更好地渲染模组所添加的新方块,它也可以用于构建光影包 | |
CanvasGroup | unity组件,用来控制一组UI下的每个元素的属性 | |
CapsuleCollider | 胶囊碰撞体,由两个半球体与一个圆柱体相连接而构成。它与胶囊 (Capsule) 基元形状相同。可以相互独立地调整胶囊碰撞器 (Capsule Collider) 的半径 (Radius) 和高度 (Height) 。它在角色控制器 (Character Controller) 中使用,适用于柱状物,也可以与其他碰撞器 (Collider) 结合用于异常形状 | |
Cinemachine | unity插件,是一个摄像系统,可以提供一种无需编写代码即可创建复杂行为的方式 | |
CriAtom开头的 | 未知,可能是音频管理相关组件 | |
Cubemap | 立方体贴图 | |
DelayedCallManager | 延迟调用管理 | 程序丨Unity3D性能优化最佳实践(三):协程 |
DynamicBone | 是一个Unity的动态骨骼插件,这个插件允许开发者指定对应的根骨骼,从而允许该骨骼的子骨骼进行物理结算,而根骨骼将不进行物理结算 | Unity【Dynamic Bone】- 关于人物模型头发、衣物等细节的处理-CSDN博客 |
FlareLayer | 光晕层,unity组件,连接到相机,使镜头光晕出现在影像中 | |
Font | 字体 | |
FullSerializer开头的 | 序列化插件,unity中用到的json文件存取工具 | |
GameObject | 获取当前脚本所挂载的游戏对象,是unity所有实体的基类 | Unity API详解——GameObject类_gameobject是component类的属性访问器-CSDN博客 |
GCHandle | 提供用于从非托管内存访问托管对象的方法,保证非托管代码执行的途中该托管类型不会被GC干掉 | |
gcloud_voice开头的 | sdk文件,主要实现语音转文字功能 | |
GraphicsSettings | 图形设置,unity中的一个组件 | Graphics Settings - Unity 手册 |
GUILayer | GUI层,unity组件,连接至相机,以启用二维 GUI 渲染 | |
ICSharpCode.SharpZipLib.Checksums.Crc32 | 解压缩工具 | |
ifix | ifix相关 | |
InputManager | 输入管理系统,unity组件。在Input Manager中你可以自定义输入轴及其相关的操作,简单的来说就是你可以自己定义操作按键,实现游戏对象的跑跳 | Input Manager - Unity 手册 |
Light | 光源,包含平行光、点光源、聚光灯、区域光 | Unity组件——Light基本参数使用及说明_unity灯光参数中文-CSDN博客 |
LitJson | LitJson —— 客户端配置文件优化策略 | |
LowerResBlitTexture | 低分辨率纹理 | |
LuaInterface | 是一个CLR库,以 .dll 的形式呈现,用于实现Lua和CLR的混合编程 | Unity热更新技术学习——LuaInterface-CSDN博客 |
MasterServerInterface | 主服务器接口 | |
Material | 用来把网格(Mesh)或粒子渲染器(Particle Renderers)贴到游戏对象上,使游戏物体呈现出对应的表面可视效果 | Unity 3D 中关于材质(Material)的具体说明_unity mater metalica-CSDN博客 |
Mesh | Mesh是指的模型的网格,3D模型是由多边形拼接而成,而多边形实际上又是由多个三角形拼接而成的。即一个3D模型的表面其实是由多个彼此相连的三角面构成。三维空间中,构成这些三角形的点和边的集合就是Mesh | Unity Mesh基础知识_mesh点-CSDN博客 |
MessagePack | MessagePack是一种高效的二进制序列化格式,可以像json一样在多个语言间进行数据的交换,但是它更快并且更小 | MessagePack简析 - 孙振超 - 博客园 |
MiHoYo.SDK | 公司sdk相关 | |
MoleMole开头的 | 是客户端写的类 | |
Other开头的 | 是引擎生成的C++对象,其中other.system.executableandDlls是最特殊的,是代码本身占的大小 | |
ParticleSystem | 粒子系统,可用于创建烟雾、气流、火焰、涟漪等效果 | 粒子系统模块介绍(Particle System) - 简书 |
PhysicMaterial | 物理材质,用于调整碰撞体对象的摩擦力和弹力效果 | unity物理学材质Physic Material - 三页菌 - 博客园 |
PhysicsManager | 物理管理器 | Physics Manager - Unity 手册 |
PigeonCoopToolkit | unity中用于处理美术效果的插件,可制作拖尾特效(trail effect)、烟迹(smoke trail)、烟羽/烟流(smoke plume) | |
PlayerSettings | 播放器设置 | 【U3D】播放器设置(PlayerSettings) - 走看看 |
PolygonCollider2D | 2D多边形碰撞体 | 2D多边形碰撞体( Polygon Collider 2D) · unity · 看云 |
proto开头的 | 是客户端和服务器通信用的网络协议对象 | |
QualitySettings | 质量设置 | Unity3D -- 质量设置(Quality Settings) - 爱码网 |
RectTransform | RectTransform 组件是UGUI对应的UI元素的基本组件,与游戏对象的基本组件Transform相似,RectTransform表示的是一个2D矩形的UI空间。RectTransform定义了UI元素的位置、旋转、大小、锚点、指点等 | 《国产一产二产三精华液》电视剧手机在线播放 - 南瓜影院 |
RenderSettings | 渲染设置,在“渲染设置”(Render Settings)窗口中,可以调整或设置选项,例如选择渲染器、将一个或多个摄影机设置为可渲染、设置渲染图像的名称、格式和分辨率,以及调整渲染的质量设置,等 | https://www.wenjiangs.com/doc/xwyiurtc |
RenderTexture | RenderTexture是unity定义的一种特殊的Texture类型,它是连接着一个FrameBufferObject的存在于GPU端的Texture | https://blog.csdn.net/qq_34562355/article/details/91881523 |
Rigidbody | Rigidbody在Unity3D中表示刚体,可以让游戏的对象受到物理引擎的控制,利用刚体,可以使物体收到推力、扭力、重力等力来模拟出真实的物理表现效果,并通过力与其他物体交互 | 【Unity】什么是刚体(Rigidbody)?刚体怎么用?_unity 刚体-CSDN博客 |
Shader | Shader其实就是专门用来渲染图形的一种技术,通过shader,程序设计人员可以自定义显卡渲染画面的算法,使画面更漂亮、更逼真,从而使画面达到我们想要的效果 | https://zhuanlan.zhihu.com/p/46745694 |
SkinnedMeshRenderer | 带蒙皮的网格渲染器 | 带蒙皮的网格渲染器 (Skinned Mesh Renderer) - Unity 手册 |
SRDebugger | unity插件,可以再真机运行的时候查看Log,或者添加一些测试功能 | 【插件】Unity插件UnitySRDebugger的简单使用-CSDN博客 |
texture2D | 二维纹理 | [Texture]详解Texture2D-CSDN博客 |
texture3D | 三维纹理 | 入门图形学:Texture3D-CSDN博客 |
UniRx | UniRx 是一个 Unity3D 的编程框架。它专注于解决时间上异步的逻辑,使得异步逻辑的实现更加简洁和优雅 | 【Unity框架】UniRx精讲(长文警告!建议收藏!) - 哔哩哔哩 |
UnityEngine开头的 | unity引擎相关 |
八.3D美术优化
优化 3D 美术资源时,我们将使用迭代过程指导你找出并消除性能问题。优化过程包含以下步骤:
1. 性能分析,即使用性能分析器测试应用程序。
2. 分析数据,查找瓶颈。
3. 确定要应用的相关优化。
4. 验证优化是否有效。
5. 如果性能令人难以接受,则返回步骤 1 并重复该过程。
以下是对上述移动游戏应用优化过程的示例:
1. 在测试时,发现游戏中存在图形问题或性能不佳的问题,在测试设备上运行并借助Unity 性能分析器进行测试以查找问题。
2. 分析性能分析器的测试结果可以帮助你隔离并确定性能问题的根源。例如,你可能会注意到,在主角进入视野后,Rendering 性能分析器模块中出现了很高的渲染尖峰。
3. 在发现该较大峰值并对其他情况进行测试后,你确定当某些模型进入视野时,游戏可能渲染了过多的顶点。
4. 将这些情况告知团队并建议为某些 3D 模型使用 LOD,以减少从远处观看时网格中的顶点数量。
5. 在模型上应用 LOD 后,再次测试游戏以验证优化是否有效
如果执行上述过程后,应用程序仍未按预期运行,则可以重新开始这一过程,通过再次对game进行性能分析来找出其他导致问题的原因。
场景测试需要注意:
-
定材质球时预估性能压力;
-
场景制作到50%时,需要看功耗、drawcall面数;
-
内存受怪物数量、女武神等因素影响,要等正式上之后再关注;
-
drawcall受材质球数量(正比)、shader pass影响。
-
场景测试80%时,制定一个标准,50%不具备参考性;策划拼的场景不受美术控制。
性能分析工具
Unity profiler:可在 Unity 编辑器或连接的设备中分析游戏运行中的场景,并显示程序将时间花在了什么地方,直观说明设备是如何处理 CPU 和 GPU 工作负载的,确定游戏中的问题区域,并发现优化性能的机会。
Frame Debugger:可帮助使用者探索游戏进行的 OpenGL ES 或 Vulkan API 调用,深入分析每次绘制调用,让你能够识别渲染缺陷、评估场景中的细节级别并找出开销巨大的着色器以进行优化。
1.几何体
3D 移动应用程序中的大多数对象很可能都是使用几何体表示的。几何体是需要处理的最常见的数据,因此有必要仔细考虑其实现和优化。几何体(即多边形网格)是构成 3D 对象形状的顶点、边和面的集合,该网格可以是汽车、环境对象、角色或 3D 应用程序中显示的任何类型的资源。
下图显示了构成 3D 对象几何体的三个元素:
顶点、边和三角形结合在一起,就形成了 3D 几何体。几何体可以像上面显示的立方体一样简单,也可以像包含数千个面的结构一样复杂。在构建 3D 应用程序时,考虑场景中几何体对性能的影响非常重要。
因为处理顶点的计算成本很高,所以使用较少的三角形时,需要 GPU 处理的顶点数也相应较少。减少要处理的顶点数量可提高整体性能,能够使应用程序在更多的设备(而不仅仅是拥有最强大 GPU 的设备)上运行。
尽量在多台不同档位的设备跑测游戏,找到需要优化的点。游戏中没有明确规定屏幕上最多可以显示多少个三角形,但同时显示在屏幕上的 3D 对象越多,每个对象可以使用的三角形就越少。如果要在屏幕上显示的 3D 对象较少,则可以为每个对象使用更多的三角形。 此外,目标设备也很重要。与较旧的移动设备相比,较新的硬件通常能够处理更复杂的几何体。
1. 1避免使用微三角形
微三角形指的是对物体或场景的最终外观没有多大用处的小三角形。
当将具有大量多边形的 3D 对象移离摄像机时,会出现微三角形问题。微三角形通常是指大小在 1-10 个像素之间的三角形。
微三角形非常小、看不见,但处理起来计算成本很高。
下图显示了一个 3D 对象靠近摄像机时使用的三角形数量(右)和其远离摄像机时使用的三角形数量(左)。
在下图中,突出显示区域中的大部分三角形由于太小而无法在移动设备上看到。因此,它们对最终外观没有多大作用:
下图突出显示了远看时柱子的斜面。当近距离观看时,斜面是没有问题的:
可以采取以下几个步骤来缓解该问题:
对于与摄像机的距离会发生改变的对象,可以使用细节级别 (LOD) ;当对象距离较远时,使用正确的 LOD 可以简化该对象。
-
在背景对象上使用较少的三角形。
-
避免使用多边形创建更精细的细节。而应结合使用纹理和法线贴图。
-
避免在具有细长三角形的对象上使用有光泽的材质,这会导致闪烁。
-
合并所有太小而无法在屏幕上看到或对最终图像没有太大价值的顶点或三角形。
-
确保三角形的内部区域大于其边缘。从技术上讲,最好让三角形接近等边。
-
尽量让三角形的面积保持在 10 个像素以上。
-
尽可能移除对象上的所有细长三角形。
尽量少用微三角形有多方面的原因:
-
GPU 必须处理所有的三角形和顶点,即使是对最终场景没有任何价值的三角形和顶点,这会浪费不必要的 GPU 算力。
-
当必须将更多数据发送到 GPU 进行处理时,内存带宽会受到负面影响。
-
所需的处理量会影响移动设备的电池寿命。因此,更少的数据意味着更长的电池寿命。
1.2 LOD-细节级别
借助细节级别 (LOD),根据对象到摄像机的距离控制为场景中每个对象渲染的顶点数量。这样做可提高应用程序的性能。使用 LOD 网格是在对象与摄像机的距离发生改变时尽可能减少对象上的多余几何体来优化应用程序的巧妙方法。
此外,LOD 还能避免上一个教程中讨论的远处对象的微三角形问题。我们建议你为每一个在应用程序运行期间与摄像机的距离会发生改变的 3D 对象提供 LOD。
下图显示了如何使用 LOD 管理降低 3D 模型的复杂性,同时在模型移离摄像机时保留适当的细节级别:
为什么要使用LOD:
当对象移离摄像机时,其细节会变得不那么明显。在 20 米远的地方,很难看出有 200 个三角形的对象与有 2,000 个三角形的对象有什么区别。你肯定不希望使用超出场景实际需要的三角形数量,因此使用 LOD 是一种优化应用程序而不带来显著影响的好方法。
使用 LOD 的其他好处包括:
-
减少了必须处理的三角形数量,因此可提高性能。
-
LOD 有助于缓解微三角形带来的问题。
观察下图可以发现,即使远处对象具有不同的多边形数,看起来也是一样的:
使用LOD时,需要注意以下几点:
-
当减少三角形数量时,我们需要关注三角形将如何影响对象的轮廓。
-
在较平坦的区域移除更多的多边形。
-
Mipmap 在功能上相当于纹理的 LOD,应与模型配合使用。我们稍后将在纹理项目中介绍 Mipmap。
-
LOD 也可用于降低着色器的复杂性。对于更远处的 3D 对象,可以进行着色器和材质优化。例如,当对象远离摄像机时,可以减少其使用的纹理数量。
何时不用LOD:
在某些情况下,不适合使用 LOD。例如,在摄像机视图和对象都是静态的或对象已使用较少的多边形的应用程序中,应避免使用 LOD。
LOD 必须保存网格数据以便实时使用,因此会带来内存开销和更大的文件大小。
下图显示了一个未使用 LOD 的场景(因为该场景是静态的)。在这种情况下,可以使用其他优化技巧,例如移除对玩家不可见的多边形:
LOD 级别数:
对象应具有的 LOD 数量没有明确规定。这取决于对象的大小和重要性。例如,动作游戏中的角色或赛车游戏中的汽车可以从使用更多的 LOD 级别获益,而不是像树这样较小的背景对象。
如果使用的 LOD 级别太少,就无法带来太大的性能提升。而且,如果级别之间的多边形缩减量跳动幅度过大,则在切换 LOD 时会显得十分突兀。如果使用的 LOD 级别太多,又会影响应用程序的性能。
此外,随着 LOD 的增多,内存使用量也会增加,因为必须在某个地方存储更多的网格,这会导致文件变大。但最大的成本是你的时间,特别是你以手动方式创建每个级别时。
2.纹理
2.1 纹理
纹理是应用于 3D 模型表面的图像,用以赋予模型细节和真实性。例如:岩石峭壁上具有的纹理和些许污垢、灰尘。纹理不仅可以显示用户将在 3D 模型上即时看到的主要细节,而且在内存等资源有限的情况下,纹理还能协同存储更复杂的特征。法线贴图是一种特殊的纹理类型,可赋予对象更精细的细节。
纹理大小、颜色空间和压缩都会影响游戏的性能:
-
纹理可以有不同的大小。减小需要较少细节的纹理的大小有助于减少带宽。
-
纹理压缩是一种图像压缩,用于在保持视觉质量的同时减小纹理数据大小。通常使用 TGA 或 PNG 等格式导出纹理。虽然这些格式比较方便使用,主要的图像软件程序都支持它们,但是与专用图像格式相比,它们的访问和采样速度较慢,因此不能在最终渲染中使用。对于 Android,有多个选项,例如自适应可伸缩纹理压缩 (ASTC)、Ericsson 纹理压缩 (ETC) 1 或 ETC2。。
2.2 纹理图集
纹理图集是一种图像,其中包含打包在一起的多个较小图像的数据。使用纹理图集,而不是一个网格对应一个纹理,可以拥有可供多个网格使用的较大纹理。
纹理图集可以对共用相同纹理图集和相同材质的多个静态对象进行批处理。批处理可减少绘制调用次数;对于 CPU 密集型的游戏,减少绘制调用可提高性能。
纹理图集可以减少移动应用程序中的绘制调用和内存使用量,是打包纹理和为 3D 移动应用程序节省宝贵资源的有效方法。
2.3 MipMapping
Mipmap 是在运行时对距摄像机的距离会发生改变的 3D 对象进行纹理优化的重要工具。Mipmap 是以逐渐降低的分辨率保存的纹理副本,可以认为 Mipmap 等同于细节级别 (LOD),但只是针对于纹理。
渲染具有 Mipmap 的纹理时,将根据像素片元在屏幕空间中占据的纹理空间大小选择适当的级别进行采样。当对象离摄像机较远时,将应用较低分辨率的纹理。当对象离摄像机较近时,将应用较高分辨率的纹理。
由于 GPU 不需要在远离摄像机的对象上渲染全分辨率纹理,因此 Mipmapping 可提高移动应用程序的性能和质量。 此外,Mipmapping 还能减少纹理锯齿,从而改善最终图像质量。纹理锯齿会在离摄像机较远的区域产生闪烁效果,如下图所示。
在导入纹理时,Unity 会根据需要自动创建 Mipmap,然后重新缩放并非 2 的幂的纹理。
3.着色器和材质
着色器和材质在确定目标硬件将如何处理和渲染 3D 对象方面起着至关重要的作用,如果材质或着色器配置不当,可能导致严重的瓶颈,使移动设备本就有限的资源变得更加紧张。
着色器是一种小程序,它告诉 GPU 如何在屏幕上绘制对象以及绘制该对象所必须进行的一切计算。对于编写着色器,有两种常见的脚本语言:高级着色语言 (HLSL) 和 OpenGL 着色语言 (GLSL)。
着色器通过定义对象外观的材质应用于游戏对象,材质用于设置着色器可用的参数。例如,材质可以指定着色器引用的颜色、纹理和数值。
3.1 光照着色器与无光照着色器
在创建着色器时,你可以决定材质将如何对光作出反应。大多数着色器可归类为光照着色器或无光照着色器。无光照着色器是速度最快、计算成本最低的着色模型。因此,如果你的目标是低端设备,请使用无光照着色器。需要考虑的关键点包括:
-
光照不影响无光照着色模型。这意味着可以避免很多计算,例如镜面反射计算。结果是渲染成本更低,或者渲染更快。
-
类似于卡通的风格化美术设计可与无光照着色良好地配合使用。在为移动平台开发游戏时,这种风格值得考虑。
与无光照着色器相比,光照着色器需要更多的处理能力。但是光照会影响光照着色器,并让表面能够产生镜面反射。 这可能是当今移动游戏中最常用的着色模型。 下图显示了有光照(左)和无光照(右)对象之间的区别:
中的塔楼应用了相同的塔楼网格和纹理,但使用了不同的着色器。光照不影响无光照着色器,因此在屏幕上渲染它们所需的计算较少,可带来更好的性能,特别是在性能较弱的设备上。
3.2.透明材质
与渲染不透明对象相比,渲染具有透明度的对象总是会使用更多的 GPU 资源。在移动平台上使用很多透明对象会影响性能,特别是多次渲染相互重叠的透明对象时,该过程称为过度绘制。
为避免过度绘制,在移动应用程序中实现任何类型的透明度之前,必须仔细评估需求。选择使用哪种类型的透明度并不总是明确的,对应用程序进行性能分析可提供更多数据帮助做出决定。
4.光照
光照是实时 3D 视觉效果最重要的方面之一,它可以营造气氛、引导交互、确定目标和目的。光照部分需要了解:
-
了解并比较静态光照与动态光照的区别。
-
了解如何在保持最佳性能的同时表现你想要的视觉效果,例如伪造光照/阴影。
-
不同的光照模式和光源类型,以便为你的 3D 场景实现最高效、最准确的光照。
4.1 光照模式
在 Unity 中,有几种不同的光照模式可供选择。这些模式与光源的移动性及其在场景中的使用方式有关。各种模式在性能方面存在差异,因此在实现光照时,这是一个重要的考虑因素。在本教程中,我们将探讨三种不同光照模式(baked、mixed 和 real-time)的优缺点。
4.2 烘焙光照
烘焙光照模式提供静态光照,也就是说,它在运行期间不会发生改变。烘焙是指在运行游戏前将光照数据存储在纹理贴图中的过程。关于烘焙光照模式,有以下重要注意事项:
-
光照和阴影被烘焙到了光照贴图中,因此在运行时无法修改。这项处理是在 Unity 中创建光照时完成的,因此不影响运行时性能。
-
阴影是静态的,如果在游戏过程中有动态或移动对象,阴影可能会显得很奇怪。
-
烘焙光照模式是我们在本指南中讨论的计算成本最低的方法。
4.3 实时光照
实时光照模式提供动态或可移动的光照。实时光照模式的主要特点如下:
-
动态光照和阴影未烘焙到光照贴图中,可在运行时进行修改。
-
实时光照模式是我们在本指南中讨论的计算成本最高的光照模式。
4.4 混合光照
混合光照模式将静态光源与移动对象结合在一起。可以将其视为其他两种方法的混合体。混合光照模式的主要特点如下:
-
提供动态的直接光照和阴影。
-
混合光照类型的光源可以参与静态物体的光照贴图烘焙过程。
-
光源会影响动态对象,包括为这些对象生成阴影。
-
可在运行时更改强度,并且仅更新直接光照。
-
混合光照模式是一种代价高昂的计算方法。
4.5 实时光源和光源类型
在构建 3D 移动应用程序时,应尝试使用烘焙光照、光照探针和材质效果处理所有的光照。当需要使用实时光源时,必须考虑要使用的实时光源类型。各种类型的计算成本存在差异:
-
方向光:方向光朝所有方向发射、无衰减,是成本最低的实时光照。通常只需要一个方向光,因为它可以照亮整个场景。这意味着,在进行前向渲染时,Unity 将始终渲染一个方向光。即使场景中没有方向光,也是如此。
-
点光源:点光源位于空间中的一个点,并在所有方向上均匀发光。
-
聚光灯:相比球形点光源,聚光灯能剔除更多对象,因此它是成本第二低的实时光照类型。限制锥体宽度,只让它照亮选定对象,以获得最佳性能。
使用聚光灯和点光源看似时尚,但也会影响性能。相比之下,方向光计算成本较低,并可照亮所有区域。小区域应使用聚光灯,大区域应使用点光源,同时还应考虑它们对性能的影响。阴影计算可能是光照计算中开销最大的部分,向各个方向投射光线会增加阴影计算所花费的计算能力。动态光源的渲染成本很高,最好不要在移动游戏中使用。有时,3D 引擎会根据所用的设备和图形 API 限制其使用。例如,在 Unity 通用渲染管线前向渲染器中,每个对象最多只能使用 8 个光源(使用 OpenGL ES 2.0 时,限制为 4 个光源)。
4.6 静态对象的光照
在为移动设备开发应用程序时,使用静态光照至关重要。静态光照在移动设备上的运行速度更快,可为用户带来更好的体验。可以通过称为烘焙的过程(在该过程中,Unity 在运行时之前执行光照计算并保存结果)保存非移动对象上的静态光照信息。
相比之下,非静态光照(如动态或实时光照)是在每一帧中计算和更新的。虽然这可以增加场景的逼真度和增强沉浸感,但其成本要高得多。因此,在使用非静态光照时,要注意在美学目标与性能之间取得平衡。
烘焙会生成称为光照贴图的单独纹理,它存储了有关光照效果的信息。该信息已缓存,因而不会在运行时产生额外的性能成本。在为移动平台构建应用时,应将尽可能多的信息烘焙到光照贴图中。
预烘焙的光照无法根据场景的动态方面进行调整。但是,烘焙光照包括场景中所有静态元素的全局光照,这让每个静态元素可以发出和接收间接反射光,使得光照更加逼真。
配置要烘焙的光照后,还应确保已优化烘焙光照贴图。 光照贴图的大小取决于烘焙时使用的设置,在移动平台上最好尽量减少内存使用,因此需要密切注意光照贴图的大小。
4.7 阴影
阴影可以为 3D 环境增加真实感,但在运行时的计算成本很高。在移动设备上,最好尝试使用着色器、纹理或其他不依赖阴影投射光照的方法伪造阴影。真实阴影的渲染计算成本很高,因此建议在动态对象上实现伪阴影,而不要使用动态光照。
实时阴影通常通过阴影贴图技术生成,渲染到阴影贴图的场景几何体的成本与绘制到其上的顶点数量相当。考虑到成本,限制场景中的阴影投射几何体数量以及实时阴影投射光照的数量非常重要。下面是实现伪阴影的一些方法:
-
使用放置在角色下方的 3D 网格、平面或四边形,并对其应用模糊的纹理。
-
编写自定义着色器以创建更复杂的模糊阴影。
下面的屏幕截图显示了使用阴影网格的阴影实现。
将光照信息直接绘制到纹理上不失为一个好方法。这样做可减少实时光照所需的额外计算。当在场景中烘焙光照时,绘制的光照还可以节省内存,因为场景需要较少的纹理存储光照贴图。
尽可能使用着色器或材质模拟光照。可以使用自定义材质模拟不同类型的光照效果。例如:可能希望角色具有边缘光照,以提高其可见性和视觉效果。对于这种情况,可以使用着色器营造光照错觉,而不是使用光源创造这种效果。