渲染 · 性能

千万级高斯点的场景,怎么在浏览器里跑顺

做单件物体的 3DGS 和做园区级的 3DGS,是两种难度。 这篇记录我们在 1193 万高斯点场景上做的事: 分块流式加载、帧时间该看哪个数、以及一次"单帧 483 毫秒卡顿"是怎么定位并消掉的。

技术 · 大场景 · 含实测数据

先立个规矩:3DGS 的帧率数字,必须带四个条件

你在网上看到的"某某场景跑到 60fps",如果不带下面这四项, 基本没有参考价值:

  1. 机型 / GPU —— M1 Pro 和 M4 Max 差好几倍
  2. 渲染分辨率 —— 分辨率减半,帧时间近乎减半
  3. 每帧实际渲染多少高斯点 —— 最关键也最常被偷换的一项
  4. 场景总点数 —— 和第三项不是一回事,能差一个数量级

业内生产系统通常用 LOD 预算把每帧压在 200 万到 400 万点, 而我们当时这份资产是单层 LOD,每帧接近全量 —— 约 1200 万点。

也就是说,同样的硬件下,按业内预算配置会快得多。 所以下面所有数字都请连带条件一起看。

第一件事:分块流式加载

1193 万高斯点的原始模型体积很大,不可能让用户等整个文件下载完。 做法是把它烘焙成八叉树分块:

  • 根索引文件 lod-meta.json 记录整棵空间树
  • 每个叶子节点是一个分块,属性压缩成 WebP 图片存储
  • 相机看向哪里,就加载哪一块

我们这份资产的规模:78 个分块、167MB。 因为按需加载,点开页面几秒内就能看到内容,而不是等 167MB 下完。

第二件事:帧时间该看哪个数

这一点比上面那条重要得多,而且很多人搞错了。

不要只看平均帧率。用户感觉到的"卡",来自偶尔出现的慢帧, 而平均帧率会把它们平均掉。

举个真实的例子:平均 30fps 听起来还行,但如果这个平均值是由 "大部分 20ms 的帧 + 偶尔一个 400ms 的帧"组成的,用户就会明显感觉到顿挫。 反过来,如果帧时间稳定在 33ms 左右,虽然帧率不高,观感却是顺的。

所以我们以 p95 / p99 帧时间作为验收标准,而不是平均 fps。

还有一个采样口径的坑

统计帧时间时,如果用"最近 60 帧"这样的帧数窗口, 在低帧率下这个窗口会横跨几十秒 —— 你看到的数字其实是很久以前的, 完全没法用来判断当前状态。

正确的做法是按时间窗口统计(比如"最近 1 秒内的所有帧")。

第三件事:消掉那个 483 毫秒的卡顿

这是我们在这份资产上遇到的最有价值的一个问题。

现象

场景加载过程中,会间歇性出现单帧长达 483 毫秒的停顿 —— 相当于画面卡住半秒。p99 帧时间被这个彻底拉坏。

定位

我们怀疑是解码的问题,而不是渲染 —— 因为纯渲染的话,帧时间应该是平稳的,不会出现孤立的尖峰。

查下来确认了:加载阶段的分块请求并发数达到了 128。 128 个 WebP 分块同时解码,在浏览器主线程上造成了一次"解码风暴", 直接把单帧顶到了 483 毫秒。

解决

不是简单地"把并发调小",而是分两段处理:

  • 首轮加载允许较高并发 —— 用户刚打开时需要尽快看到内容
  • 首轮完成之后把并发降到 8 —— 此时用户已在浏览, 后续分块可以慢慢来,不影响体验

效果:尖峰从 3 次降到 0 次,最大帧时间 ≤ 75 毫秒。

这个结论是实测出来的,而且它指向一个更普遍的道理: 大场景的性能问题,往往不在渲染,而在数据加载和解码。 先量一量,别急着优化渲染器。

第四件事:分辨率自适应

当帧时间超标时,主动降低渲染分辨率来保住流畅度;有余量了再升回来。

这里有个不踩一次就不知道的坑:改完像素比之后,必须调用一次 syncCanvasSize(),否则设置不会立即生效 —— 我们因为这个白查了一段时间。

另外,我们的实测结论是:分辨率比"点数预算"更有效。 在当时的工况下,降低点数预算对帧时间影响有限,而降分辨率效果立竿见影。 所以策略定为:分辨率为主,点数预算保底。

实测数据

同一台机器(Apple M1 Pro,16 核 GPU)、同一场景、同一相机路径, 在浏览器中以 WebGPU 后端渲染:

指标自适应关自适应开变化
帧时间 avg25.0 ms16.7 ms−33%
p9531.4 ms17.9 ms−43%
帧率39.960.0+50%
渲染像素比1.000.34—

场景 11,933,631 高斯点(单层 LOD,78 分块)· 每帧约 1200 万点 · 分辨率 1400×900 · Chrome / WebGPU · Apple M1 Pro(16 核 GPU)

注意最后一行:自适应是用渲染分辨率换来的帧率。 这是有代价的取舍,不是免费的午餐 —— 所以它应该是"保底手段",而不是默认状态。

还没做完的事

诚实地说几条我们还在做的:

  • 多级 LOD。当前资产是单层,每帧接近全量点。 做真正的多级 LOD 后,同样的观感下每帧渲染量能大幅下降
  • 每帧点数 vs 帧时间的曲线。我们正在补一条 (100 万 / 200 万 / 400 万 / 800 万 / 1200 万 五档), 这样才能和业内公开的预算口径直接对比
  • 移动端。目前的调优主要在桌面浏览器上验证

小结

  1. 帧率数字必须带条件,否则不可比
  2. 看 p95 / p99,不看平均 fps
  3. 统计要按时间窗口,不按帧数窗口
  4. 大场景的瓶颈常在加载与解码,不一定是渲染
  5. 分辨率是比点数预算更强的旋钮,但用它是取舍不是免费

需要做大场景的在线展示?

我们做园区级、建筑级的实景重建与浏览器渲染, 包括流式加载与性能调优。说说你的场景和用途,我们评估可行性。