08. 点云渲染
在自动驾驶(HD Map 编辑器)、数字孪生、智慧城市以及 GIS 领域,点云(Point Cloud)是最基础且最重要的数据表达形式之一。与由多边形面片组成的传统 Mesh 不同,点云是由海量三维坐标点集合构成的,每个点通常包含位置(XYZ)、颜色(RGB)、法线(Normal)以及强度(Intensity)等信息。
1. 主流点云文件格式
不同的采集设备(激光雷达 LiDAR、倾斜摄影、深度相机)和处理流会产出不同格式的点云文件。
1.1 文本/简单二进制格式
- XYZ / CSV / TXT:最原始的格式。每行记录一个点的数据,以逗号或空格分隔(如
X Y Z R G B)。- 优点:人类可读,极易解析。
- 缺点:体积巨大,加载极慢,没有拓扑或元数据信息。
- PLY (Polygon File Format):斯坦福大学开发,不仅支持多边形,也广泛用于点云。支持 ASCII 和二进制。
- 特点:格式灵活,可自定义属性(如附带法线、曲率等)。在 3D 扫描领域非常常见。
- PCD (Point Cloud Data):由 PCL (Point Cloud Library) 开源库定义的标准格式。
- 特点:专为点云设计,支持 ASCII、二进制以及二进制压缩(LZF)。广泛应用于机器人和自动驾驶领域。
1.2 专业测绘/工业格式
- LAS (LASer format):ASPRS (美国摄影测量与遥感协会) 制定的行业标准格式,专为三维点云(特别是机载 LiDAR)设计。
- 特点:结构严谨,包含丰富的元数据(如雷达回波次数、分类、GPS 时间)。
- LAZ:LAS 的无损压缩版本,体积通常只有 LAS 的 10% - 20%,是目前网络传输和存储最主流的格式。
2. Three.js 中的点云加载与渲染
在 Three.js 中,点云的基础渲染单元是 THREE.Points 和 THREE.PointsMaterial。
2.1 基础加载器
Three.js 官方的 examples/jsm/loaders/ 提供了对应格式的加载器:
import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader.js";
import { PCDLoader } from "three/examples/jsm/loaders/PCDLoader.js";
// 以加载 PCD 为例
const loader = new PCDLoader();
loader.load("pointcloud.pcd", function (points) {
// Points 材质设置
points.material.size = 0.05; // 点的大小
points.material.color.setHex(Math.random() * 0xffffff);
scene.add(points);
});
2.2 性能瓶颈
原生 Three.js 加载器处理点云有一个致命缺陷:它是一次性将整个文件解析为一个 THREE.BufferGeometry。
- 内存爆炸:几百万个点(几十MB的 LAZ 文件)解压到显存中,可能会占用数百 MB 的 RAM 和 VRAM。
- 渲染卡顿:如果点云达到千万级,原生
THREE.Points强行一次性把千万个点提交给 GPU,FPS 会断崖式下跌。 - 加载阻塞:文件没下载并解析完之前,屏幕上什么都没有。
当点云规模达到 千万级甚至百亿级(几十GB 甚至 TB 级别)时,原生 Three.js 彻底无能为力。这就引出了工业级解决方案——Potree。
3. 为什么 Potree 能成为大型点云首选?
Potree 是一个基于 WebGL 的开源大规模点云渲染器(底层最初基于 Three.js 开发)。它是目前 Web 端处理海量(百亿级)点云的绝对霸主和事实标准。
Potree 能做到“不管你有多少亿个点,我都能保持 60 帧流畅渲染”,其核心奥秘在于:Octree(八叉树)空间划分 + LOD(细节层次) + Out-of-Core(核外/按需加载)。
3.1 核心原理一:预处理与八叉树划分 (Octree Partitioning)
Potree 渲染前,必须先使用 PotreeConverter 工具对原始点云(LAS/LAZ/PLY)进行预处理。
- 它将整个三维空间切分成一个巨大的包围盒。
- 如果盒子里点太多,就把它等分成 8 个子盒子(八叉树)。
- 递归这个过程,直到每个叶子节点的点数低于一个阈值。
预处理后,会生成一个目录结构,包含一个元数据文件(.json 或 cloud.js)和成百上千个小的二进制块文件(.bin 或 .laz)。
3.2 核心原理二:视点相关的细节层次 (LOD - Level of Detail)
Potree 会在八叉树的每一层都提取一个“降采样”的点云代表。
- 根节点(最顶层)包含了整个点云最稀疏的轮廓(比如 1000 个点)。
- 叶子节点(最底层)包含了最密集的原始细节。
在渲染时,Potree 根据摄像机的位置动态决定加载哪一层:
- 离得远的区域,只渲染顶层的稀疏点(反正在屏幕上也只有几个像素大)。
- 摄像机凑近的区域,才去渲染底层的密集点。
3.3 核心原理三:按需/核外加载 (Out-of-Core)
因为做了八叉树切块和 LOD,Potree 实现了“按需加载”(Streaming)。
- 初始化时,只下载几十 KB 的根节点数据,瞬间就能在屏幕上看到点云的全貌(虽然很稀疏)。
- 当用户移动视角时,Potree 会实时计算视锥体(Frustum)覆盖了哪些八叉树节点,然后只去请求这些节点的高层级数据文件。
- 这种策略使得 Web 端可以毫无压力地浏览 100GB 的点云数据,因为同一时刻驻留在内存和显存里的数据量被严格控制在一定预算内(比如最多只允许 200 万个点)。
3.4 高级渲染技术 (Splatting)
如果只画像素点,放大后点云会显得很稀疏,能看到后面的背景。 Potree 提供了 Point Splatting 和 EDL (Eye Dome Lighting) 技术:
- Splatting:把点渲染成有大小的圆盘或插值面片,填补点与点之间的空隙,看起来像连续的表面。
- EDL:一种无需法线数据的屏幕空间着色技术,通过深度缓冲(Depth Buffer)检测边缘并加深阴影,让完全没有光照的点云也具备极强的立体感和深度轮廓。
4. 总结与选型建议
- < 100万点(小物件扫描、简单的物体点云)
- 方案:直接使用 Three.js 的
PLYLoader或PCDLoader配合THREE.Points。 - 优势:简单、无额外依赖、可随心所欲定制材质。
- 方案:直接使用 Three.js 的
- 100万 - 千万级以上(建筑、城市街道、自动驾驶 HD Map)
- 方案:必须使用 Potree (或基于 Potree 衍生的库如
potree-core)。 - 工作流:原始点云 (LAZ) $\rightarrow$
PotreeConverter预处理 $\rightarrow$ 静态文件服务器部署 $\rightarrow$ Web 端 Potree 加载。 - 优势:极速首屏、无限容量、自带测距/裁剪/高亮等成熟工具。
- 方案:必须使用 Potree (或基于 Potree 衍生的库如