4D点云加载处理方案

4 阅读3分钟

一次请求,多帧切换: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 标注与审阅工具