维持 FPS 帧率稳定的实战技巧
为什么 AR 应用特别容易掉帧
普通 3D 游戏可以专注于"把画面渲染出来"——60fps 就够好了。AR 应用每帧需要做:
摄像头采集(~30ms)+
ARCore SLAM + 平面检测 + 光照估算(~10ms,随帧数增加)+
深度图生成(启用 Depth API 时 +10~20ms)+
你的 3D 渲染(~16ms @ 60fps)+
遮挡渲染(额外开销)
──────────────────────────
总计:很容易超过 33ms(30fps),更别说 16ms(60fps)
ARCore 本身在不断优化,但开发者的 3D 渲染是最大的变量——同样的硬件,好的渲染管线和差的可能差 2~3 倍帧率。
帧率诊断:先搞清楚谁在拖后腿
方法一:Android Profile GPU Rendering
开发者选项 → Profile GPU Rendering 选择 "On screen bars"——屏幕上会实时显示每帧渲染时间的直方图,彩色分解 CPU/GPU/IO 各部分耗时。
方法二:Unity Profiler
Unity 的 Profiler 是最强大的性能分析工具:
- CPU Usage(Detailed):看哪些函数占时间,重点关注 Camera.Render 和你的材质 Shader
- Rendering:Draw Call 数、SetPass Call 数、Batches
- Memory:纹理、Mesh 内存占用
方法三:ARCore Debug Overlay
ARCore 自带调试叠加层——在 Session 创建时开启:
session.configure(
Session.Config(context).apply {
// 在屏幕上显示 SLAM、平面检测等状态
setShowDiagnostics(true)
}
)
Debug Overlay 会显示:
- 当前帧 SLAM 跟踪质量(Good / Bad / Lost)
- 检测到的特征点云(Point Cloud 可视化)
- 检测到的平面(彩色半透明)
- 每一帧的处理耗时
渲染层面优化(最大收益)
优化一:减少 Draw Call
AR 场景的 Draw Call 数量直接决定 GPU 忙碌程度。主要手段:
| 手段 | 说明 | 效果 |
|---|---|---|
| 静态批处理 | 把多个固定不动的静态物体合并渲染 | 减少独立物体的 Draw Call |
| GPU Instancing | 同类物体一次 Draw Call 渲染多个实例 | 适合大量重复物体(如草地、栏杆) |
| 材质合并 | 把多张 2K 纹理图集成为一张 4K Texture Atlas | 减少 Material 切换开销 |
| 减少透明物体 | 透明物体需要额外的排序和渲染 | 数量控制在 5 个以内 |
| 简化材质 | 关闭不需要的 Shader Pass(如 ShadowCasterPass) | 每少一个 Pass 省一次 Draw Call |
优化二:降低分辨率(最简单有效的 AR 优化)
AR 应用最激进的优化手段——把渲染分辨率降到物理屏幕分辨率的 75% 甚至 50%,GPU 开销直接降低到原来的 56% 或 25%。
// Android 原生:设置渲染分辨率
glSurfaceView.setEGLContextClientVersion(2)
glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
// Unity:URP Asset 中设置 Render Scale
// 0.75 = 75% 分辨率,UI 分辨率不受影响
75% 分辨率在大多数手机上视觉效果差别不明显(AR 摄像头画面已经占了大部分屏幕),但能显著提升帧率。
优化三:遮挡渲染的成本控制
遮挡渲染需要写深度缓冲——对低端设备来说这是额外开销。分级策略:
| 设备档次 | 遮挡策略 |
|---|---|
| 旗舰(Pixel 5+、iPhone 12+) | 完整遮挡 + Depth API |
| 中端 | 简化遮挡(只遮挡大型平面) |
| 低端 | 关闭遮挡 |
用代码自动判断:
// 检查 GPU 性能等级
val gpuLevel = getGpuPerformanceLevel()
when (gpuLevel) {
PERFORMANCE_HIGH -> enableFullOcclusion()
PERFORMANCE_MEDIUM -> enableSimplifiedOcclusion()
PERFORMANCE_LOW -> disableOcclusion()
}
ARCore 层面优化
减少 Trackable 数量
// 每一帧的 session.getAllTrackables<T>() 是 O(n) 开销
// 如果有几百个 Trackable(大量平面 + 特征点 + Augmented Image)
// 每次遍历都会花不少时间
// 优化:
// 1. 定期清理已经合并或停止跟踪的 Plane
// 2. 限制同时活跃的 Augmented Image 数据库大小
// 3. 不再需要的 Anchor 及时 detach
关闭不需要的 API
每个启用的 ARCore API 都有计算开销:
session.configure(
Session.Config(context).apply {
// 只开用到的 API
setLightEstimationMode(LightEstimationMode.AMBIENT_INTENSITY) // 只开基础光照
// setLightEstimationMode(LightEstimationMode.ENVIRONMENTAL_HDR) // HDR 关掉更省
// 不做 Hit Test 就关掉平面检测
// setPlaneFindingMode(PlaneFindingMode.DISABLED)
}
)
帧处理频率
ARCore 默认以 30fps 处理摄像头画面——不需要强行提高到 60fps:
- 摄像头硬件通常只有 30fps
- 强行 60fps 会导致重复帧,SLAM 性能反而下降
- 你的 3D 渲染可以独立于摄像头帧率,但 ARCore 处理不要超过 30fps
渲染管线优化清单(按影响排序)
| 优先级 | 优化项 | 潜在 FPS 提升 |
|---|---|---|
| 1 | 降低渲染分辨率到 75% | 50%+ |
| 2 | 减少 Draw Call(批处理 / Instancing) | 20~40% |
| 3 | 简化材质 Shader(减少 Pass / 关闭 Shadow) | 15~25% |
| 4 | 减少透明物体数量 | 10~15% |
| 5 | 纹理压缩 + Mipmap | 10%+ |
| 6 | 遮挡渲染分级(低端关闭) | 10~20% |
| 7 | 关闭 HDR 光照估算 | 5~10% |
| 8 | 清理无用 Trackable 和 Anchor | 3~5% |