一次请求,多帧切换:4D 点云的前端实现方案详解
在 4D 点云(时间序列点云)的 Web 可视化中,经常会遇到一个现象:
网络面板里只看到一次点云请求,却可以在界面中自由切换不同帧
这并不是缓存魔法,也不是后端按帧偷偷下发数据,而是一种典型的 4D 点云处理方式。
本文将从数据组织、前端渲染和切帧逻辑三个层面,完整解释这一方案。
一、整体思路概览
核心思路可以总结为一句话:
一次性下载融合后的完整点云,每个点自带帧信息,切帧只是改变点的可见性
也就是说:
- 网络层:只请求一次
- 内存层:一次加载所有帧的点
- 渲染层:根据帧号过滤显示
二、请求流程:只下载一次完整点云
1️⃣ 获取任务与配置
GET /editor/jobInfo
用于获取点云任务的基本配置,例如:
- 帧数
- 点云文件地址
- UID 规则
2️⃣ 下载融合后的大点云文件
GET oss.com/0.pcd
这个 0.pcd 并不是某一帧的数据,而是:
- 所有帧融合后的点云
- 每个点代表某一时刻(frame)的空间采样
点的总数 = 所有帧点数之和。
3️⃣ 下载 UID 映射文件
GET oss.com/0_uid.npy
UID 文件中记录了每个点的唯一标识,并且 UID 本身编码了帧信息。
三、关键设计:每个点都知道自己属于哪一帧
1️⃣ 从 UID 中解析 frameIndex
UID 的高位编码了帧号,例如:
const frameIndex = (uid >>> 21) + 1;
这意味着:
- 不需要额外的 frame 文件
- 帧信息是点的固有属性
2️⃣ 将 frameIndex 写入点云属性
在 Three.js / WebGL 中:
pcd.geometry.setAttribute(
'frameIndex',
frameIndexArray
);
这样,每一个点在 GPU 层面都携带了:
- position
- color
- frameIndex
四、切帧的本质:改变点的可见性
❌ 错误理解
切帧 ≠ 重新请求点云
切帧 ≠ 重新构建 geometry
✅ 正确做法:基于 frameIndex 过滤
以切换到第 10 帧为例:
function filterPcd(startFrame, endFrame) {
for (let i = 0; i < 点云总数; i++) {
const frame = frameIndex[i];
visible[i] = (frame >= startFrame && frame <= endFrame) ? 1 : 0;
}
}
本质就是:
- 同一份 geometry
- 不同的 visible / alpha / discard 状态
渲染阶段
在 Shader 或 CPU 侧:
- 只渲染
visible === 1的点 - 其余点不参与绘制
五、为什么这种方式性能好?
1️⃣ 网络层优势
- 只请求一次
- 无切帧请求抖动
- 非常适合大规模点云
2️⃣ GPU 友好
- Geometry 不变
- Attribute 更新成本低
- 可以进一步放到 Shader 中用
discard
3️⃣ 时间连续性天然存在
-
多帧点云在空间上是连续的
-
支持:
- 播放
- 拖动时间轴
- 帧区间显示
六、这就是 4D 点云的典型处理方式
总结一下整个方案的关键点:
- ✅ 一次性下载完整的 4D 点云(所有帧)
- ✅ 每个点自带
frameIndex - ✅ 切帧不请求新数据
- ✅ 切帧只是改变点的可见性
- ✅ 前端完全掌控时间维度
你看到的“只请求一次点云,却能切多帧”,本质就是这种设计。
七、适用场景
- 自动驾驶点云回放
- SLAM / 重建过程可视化
- 多帧融合点云编辑
- 4D 标注与审阅工具