先说结论,省得你往下翻:2025 年了,Quest 平台上一体机 VR 应用掉帧,大部分情况不是你的模型面数太多,是 Draw Call 和 Overdraw 在偷偷吃 GPU 时间,而这两个东西在编辑器里根本看不出来。
去年 3 月我接了个工业培训的项目。甲方给了一套从 SolidWorks 导出来的厂房模型,转成 FBX 之后 82 万三角面,材质球一百四十多个。他们要求跑在 Quest 2 上,因为公司两百多台设备全是 Quest 2,不可能换。我第一版做完,Unity 编辑器里 Play 模式跑得挺顺,60 帧稳的,当时还挺得意。打包到真机上,一戴上头显,转头的时候画面直接撕裂,OVR Metrics 显示 43 fps,GPU 利用率 99%。那天晚上我在办公室坐到两点。后来才搞明白,编辑器里的 Game 视图根本不渲染双目立体,你看到的性能数据大概要乘以 1.8 到 2.2 才是真机水平。这个倍数不是玄学,是渲染两次场景加上畸变校正的成本。
先搞清楚到底是谁在吃你的帧
具体说下定位过程。Meta 有个 OVR Metrics Tool,可以 sideload 到 Quest 上,实时叠加显示 FPS、GPU 利用率、CPU 利用率、温度、掉帧次数。但它只告诉你「不行」,不告诉你「为什么不行」。真有用的是 ovrgpuprofiler,Meta 提供的命令行工具,通过 ADB 跑:
adb shell ovrgpuprofiler -t 5 -d
它会吐一份 Perfetto 能打开的 trace,里面能看到每个 render pass 的 GPU 时间。我跑完一看,光不透明物体那个 pass 就占了 8.2ms,而 Quest 2 跑 72Hz 的预算是 13.8ms 总共(1000 除以 72)。剩下不到 6ms 要跑透明、UI、后处理、合成,根本不够分。
再往下拆,两个问题。一是 Single Pass Instanced 没开,Unity 的 XR 设置里默认是 Multi Pass,这个模式下一帧要渲染两遍场景,Draw Call 直接翻倍。我那个场景 140 个材质,Multi Pass 下就是 280 个 DC,GPU 光提交命令就累死了。改成 Single Pass Instanced,DC 降到 143。二是纹理,美术给的贴图全是 2K PNG,Unity 默认导入成 RGBA32 不压缩。120 张贴图乘 2048 乘 2048 乘 4 字节,算下来 1.9GB 显存。Quest 2 总共 6GB 内存,系统占掉 1.5GB,Unity 应用分到的图形内存到 2GB 就得小心了。改成 ASTC 6x6 之后,128 bit 一个 6x6 块,大概 3.56 bit/pixel,也就是 0.44 字节/像素,加 mipmap 之后总占用掉到 300MB 以内。一下活了。
前后对比大概是这样的:Draw Call 280 降到 143,纹理显存 1.9GB 降到 280MB 左右,不透明 pass 的 GPU 时间从 8.2ms 掉到 3.4ms,最终帧率从 43 上到 71 到 72 之间浮动。固定注视点渲染我开了 Medium 档,边缘分辨率降低,Meta 官方说能省 25% 到 35% 的像素着色开销,我实测大概省 18%,没那么多但也不亏。
Unity 还是 Unreal,这事没你想的那么重要
每次群里有人问这个,底下就开吵。我的看法比较扫兴:对九成的一体机项目来说,这个选择在 2025 年基本已经被平台替你做好了。
原因挺直接。Quest 3 用高通 XR2 Gen 2,Adreno 740 的 GPU,移动端。Unreal 的杀手锏 Nanite 和 Lumen 在 Android Vulkan 后端基本用不了。Nanite 从 UE 5.3 开始有移动端实验性支持,但 Quest 上官方不推荐,Lumen 更别提。你把那套开了,帧率直接腰斩。关掉之后,UE 相对 Unity 剩下的优势主要是材质编辑器和 Sequencer,而 VR 项目里这俩用得都不多。
反过来 Unreal 在 Quest 上的劣势挺实在。包体,一个空的 UE5 VR 模板 APK 打出来 200MB 起,Unity URP 空场景 40MB 左右。启动时间,UE 那个 shader 编译和资源加载,Quest 2 上冷启动我测过十几秒,Unity 大概三秒多。迭代速度,UE 改一行 C++ 要重新编译,热重载在 Android 上基本是摆设。但我不觉得这是「Unity 赢了」。准确的说法是:在移动 VR 这个约束下,Unity 的「穷」反而变成了优点。它是被逼的,不是设计得好。
PCVR 完全是另一回事。SteamVR 那边 Unreal 用得其实比 Unity 多,特别是建筑可视化和汽车配置器这类画面向的。跑在 RTX 4070 上你有 12ms 以上的帧预算,Nanite 和 Lumen 能开,画质差距立刻出来。所以真正的分水岭不是引擎,是你要不要碰移动端的资源约束。
OpenXR 统一了接口,但没统一能力
2021 年之前,Unity 里做 Quest 要 Oculus XR Plugin,做 Pico 要 Pico SDK,做 Vive 又是 OpenVR,一个项目里 SDK 打架是常事。现在 OpenXR 基本统一了,Unity 2021 LTS 之后在 XR Plugin Management 里勾一下,一个运行时管三家。
但 OpenXR 有个天生的毛病:它暴露的是「最低公约数」,各家平台的独有特性它管不了。比如 Quest 的 Application SpaceWarp,OpenXR 里就没有标准接口,你还是得引 Meta XR Core SDK 才能调。Pico 的注视点渲染配置参数也不一样。
Application SpaceWarp 这个值得单说。原理是你的应用按 45fps 渲染真实帧,系统根据上一帧和头显位姿变化,用运动矢量插值出中间帧,最终以 90fps 提交给显示。等于用一半渲染成本换视觉上的 90Hz。Meta 官方数据说能省 30% 到 50% 的 GPU 时间。我实测下来,运动平缓的场景(厂房漫游那种)效果很好,45 到 90 看不出来。但快速转头的时候,物体边缘有明显拖影,高对比度的细线条尤其严重。所以在工业培训里那些仪表盘读数上,用了 AppSW 之后数字会糊。我的建议是:场景里有大量快速头部运动,或者用户要盯着小字看,别用。
最贵的成本,没人愿意写进教程
VR 开发真正烧钱的不是引擎授权也不是头显,是迭代循环。
你在编辑器里按 Play,看到的画面是假的——没有双目,没有 ATW/ASW,没有畸变校正,没有发热降频。真机测一次,从 Build APK(大场景 Unity 要 8 到 15 分钟)到 sideload 到戴上头显看,一套下来 20 分钟。一上午你只能试八次。
我的应对办法是把能在编辑器里验证的全部先验证完(交互逻辑、UI 布局、数据流),只在必须真机验证的时候(渲染、性能、佩戴舒适度)才打包。另外开 Quest 的开发者模式配 ADB,adb install -r 覆盖安装比卸载重装快很多。还有个技巧是 Unity 的 Addressables 加远程资源包,美术资源改完不用重打 APK。
发热也得提一句。Quest 3 连续跑 20 分钟,SoC 温度上去会自动降频,我实测帧率能从 72 掉到 60 左右。所以别按刚开机的性能定预算,留 15% 到 20% 的余量。
这行有个很奇怪的现象:教程都在教你搭场景、写交互、做 UI,但真正让项目死掉的,是那些没人写进教程的东西——Draw Call、显存、发热曲线、还有那二十分钟一次的迭代循环。