cesium 3DTiles之pnts格式详解

Point Cloud

1 概述

点云(Point Cloud)瓦片格式用于高效流式传输大规模点云数据,常用于 3D 可视化中。每个点由位置(Position)和可选的属性定义,这些属性用来描述点的外观(如颜色、法线等)或应用特定的元数据。

在 3D Tiles 的术语中,每个点被称为一个"特征"(Feature)。Point Cloud 瓦片本身是一个二进制块,采用小端字节序(Little Endian)。

2 布局

Point Cloud 瓦片由头部(Header)和正文(Body)两部分组成。以下是 Point Cloud 布局的简图(虚线表示可选字段):

| Header | Body (Feature Table + Batch Table) |
2.1 填充

瓦片的字节长度(byteLength)必须对齐到 8 字节边界。同时,瓦片中的特征表(Feature Table)和批处理表(Batch Table)也必须遵循各自的填充要求。

3 头部(Header)

头部是一个 28 字节的数据结构,包含以下字段:

字段名 数据类型 描述
magic 4 字节 ANSI 字符串 固定值 "pnts",用于标识 Point Cloud 瓦片的内容。
version uint32 Point Cloud 格式的版本号,目前版本为 1。
byteLength uint32 整个瓦片的字节长度(包含头部和正文)。
featureTableJSONByteLength uint32 特征表 JSON 部分的字节长度。
featureTableBinaryByteLength uint32 特征表二进制部分的字节长度。
batchTableJSONByteLength uint32 批处理表 JSON 部分的字节长度。0 表示没有批处理表。
batchTableBinaryByteLength uint32 批处理表二进制部分的字节长度。如果批处理表 JSON 长度为 0,则该值为 0。

头部后面紧接着的是正文部分,它由特征表(Feature Table)和批处理表(Batch Table)组成。

4 特征表(Feature Table)

特征表包含每个瓦片和每个点的属性,这些属性定义了如何渲染点云数据。每个点的数据通过不同的语义(Semantics)进行标识,用于描述点的位置、颜色、法线等属性。

4.1 语义(Semantics)
4.1.1 点的语义(Point Semantics)

这些语义映射到一个特征值数组,用于定义每个点的属性。所有语义的数组长度必须相同,且等于点的数量。每个语义的值都是对特征表二进制部分的引用,而不是嵌入在特征表 JSON 头部中的。

  • 如果同时定义了 POSITIONPOSITION_QUANTIZED,则使用高精度的 POSITION
  • 如果同时定义了 NORMALNORMAL_OCT16P,则使用高精度的 NORMAL

常见的点语义包括:

语义名 数据类型 描述 必需
POSITION float32[3] 定义点的位置,包含 x、y 和 z 坐标。
POSITION_QUANTIZED uint16[3] 定义量化的点位置,包含 x、y 和 z 坐标的量化值。
RGBA uint8[4] 定义点的 RGBA 颜色值。
RGB uint8[3] 定义点的 RGB 颜色值。
RGB565 uint16 16 位压缩 RGB 颜色,提供 5 位红色、6 位绿色、5 位蓝色。
NORMAL float32[3] 定义点的法向量。
NORMAL_OCT16P uint8[2] 定义点的法向量,使用 16 位精度的八度编码。
BATCH_ID uint8, uint16, uint32 定义点的批处理 ID,用于从批处理表中检索元数据。
4.1.2 全局语义(Global Semantics)

这些语义定义了所有点的全局属性。

语义名 数据类型 描述 必需
POINTS_LENGTH uint32 渲染的点的数量。每个语义的数组长度应与该值相同。
RTC_CENTER float32[3] 定义点的位置相对于中心的偏移量。
QUANTIZED_VOLUME_OFFSET float32[3] 定义量化体积的偏移量。
QUANTIZED_VOLUME_SCALE float32[3] 定义量化体积的缩放值。
CONSTANT_RGBA uint8[4] 定义所有点的常量 RGBA 颜色。
BATCH_LENGTH uint32 定义唯一的 BATCH_ID 数量。

4.2 点的位置(Point Positions)

4.2.1 坐标参考系统(Coordinate Reference System, CRS)

3D Tiles 使用的是右手坐标系,具有三个轴(x, y, z),并且 x 和 y 的叉乘结果为 z。3D Tiles 定义了局部坐标系统中的 z 轴为上(up)轴。这意味着,在局部笛卡尔坐标系统中,z 轴指向上方。

4.2.2 RTC_CENTER

点的位置可以相对于一个中心点进行定义,以提高渲染的精度(见 "Precisions")。如果定义了 RTC_CENTER,则所有点的位置都相对于该中心点进行计算。这种方法尤其适用于高精度渲染,允许对整个点云的变换应用更加精确的操作。

4.2.3 量化位置(Quantized Positions)

如果未定义 POSITION,则可以使用 POSITION_QUANTIZED 来存储点的位置,这些位置是相对于量化体积(Quantized Volume)定义的。量化体积通过偏移量(offset)和缩放因子(scale)来映射量化位置到局部坐标空间。

如果既没有定义 POSITION 也没有定义 POSITION_QUANTIZED,则该瓦片无需渲染。

量化体积的定义包括偏移量和缩放因子,分别存储在全局语义 QUANTIZED_VOLUME_OFFSETQUANTIZED_VOLUME_SCALE 中。如果没有定义这些全局语义,就无法使用 POSITION_QUANTIZED

量化位置到局部空间的映射公式如下:

POSITION = POSITION_QUANTIZED * QUANTIZED_VOLUME_SCALE / 65535.0 + QUANTIZED_VOLUME_OFFSET

4.3 点的颜色(Point Colors)

如果定义了多个颜色语义,则颜色的优先顺序为:RGBA > RGB > RGB565 > CONSTANT_RGBA。例如,如果一个瓦片的特征表包含 RGBACONSTANT_RGBA 属性,运行时将使用 RGBA 作为每个点的颜色。

如果没有定义任何颜色语义,运行时可以使用应用程序定义的默认颜色来渲染点。

无论如何,都可以使用 3D Tiles 样式(Style)在运行时改变最终渲染的颜色和其他视觉属性。

4.4 点的法线(Point Normals)

每个点的法线是一个可选属性,能够通过启用光照、隐藏表面去除等渲染技术来提高点云的视觉质量。法线将在应用瓦片变换的逆转置矩阵后进行变换。

4.4.1 八度编码法线向量(Oct-encoded Normal Vectors)

八度编码(Oct-encoding)是一种高效表示独立单位向量的方式。具体而言,八度编码将单位法线向量存储为无符号且未归一化的范围([0, 255]),并在运行时将其映射到有符号归一化范围([-1.0, 1.0])。

Cesium 中提供了一个实现,用于编码和解码这些单位向量,相关模块为 AttributeCompression

4.5 批处理点(Batched Points)

点云中的不同特征可以通过 BATCH_ID 语义进行批处理。例如,一个房间的门的所有点会分配相同的 BATCH_ID,而窗户的点则会有不同的 BATCH_ID。这对于按对象进行拾取(picking)或存储应用程序特定的元数据非常有用,特别是在声明式样式(styling)和应用程序用例(如填充用户界面、发出 REST API 请求等)中。

BATCH_ID 语义可以具有 UNSIGNED_BYTEUNSIGNED_SHORTUNSIGNED_INT 的数据类型。如果没有指定 componentType,则默认为 UNSIGNED_SHORT

全局语义 BATCH_LENGTH 定义了唯一的 BATCH_ID 数量,类似于Batched 3D Model

4.6 示例(Examples)

4.6.1 只有位置(Positions Only)

这是一个简单的示例,包含四个点,它们位于单位长度正方形的四个角上:

javascript 复制代码
var featureTableJSON = { 
  POINTS_LENGTH: 4, 
  POSITION: { 
    byteOffset: 0 
  } 
}; 

var featureTableBinary = new Buffer(new Float32Array([ 
  0.0, 0.0, 0.0,  
  1.0, 0.0, 0.0, 
  0.0, 0.0, 1.0, 
  1.0, 0.0, 1.0 
]).buffer);
4.6.2 位置和颜色(Positions and Colors)

以下示例包含四个点(分别为红色、绿色、蓝色和黄色),它们的位置是相对于中心定义的:

javascript 复制代码
var featureTableJSON = { 
  POINTS_LENGTH: 4, 
  RTC_CENTER: [1215013.8, -4736316.7, 4081608.4], 
  POSITION: { 
    byteOffset: 0 
  }, 
  RGB: { 
    byteOffset: 48 
  } 
}; 

var positionBinary = new Buffer(new Float32Array([ 
  0.0, 0.0, 0.0,  
  1.0, 0.0, 0.0,  
  0.0, 0.0, 1.0,  
  1.0, 0.0, 1.0 
]).buffer); 

var colorBinary = new Buffer(new Uint8Array([ 
  255, 0, 0, 
  0, 255, 0, 
  0, 0, 255, 
  255, 255, 0 
]).buffer); 

var featureTableBinary = Buffer.concat([positionBinary, colorBinary]);
4.6.3 量化位置和八度编码法线(Quantized Positions and Oct-encoded Normals)

这个示例中,四个点将有指向上方的法线向量 [0.0, 1.0, 0.0],并且它们位于一个量化体积的角上,量化体积的范围从 -250.0 到 250.0 单位:

javascript 复制代码
var featureTableJSON = { 
  POINTS_LENGTH: 4, 
  QUANTIZED_VOLUME_OFFSET: [-250.0, 0.0, -250.0], 
  QUANTIZED_VOLUME_SCALE: [500.0, 0.0, 500.0], 
  POSITION_QUANTIZED: { 
    byteOffset: 0 
  }, 
  NORMAL_OCT16P: { 
    byteOffset: 24 
  } 
}; 

var positionQuantizedBinary = new Buffer(new Uint16Array([ 
  0, 0, 0,  
  65535, 0, 0,  
  0, 0, 65535,  
  65535, 0, 65535 
]).buffer); 

var normalOct16PBinary = new Buffer(new Uint8Array([ 
  128, 255, 
  128, 255, 
  128, 255, 
  128, 255 
]).buffer); 

var featureTableBinary = Buffer.concat([positionQuantizedBinary, normalOct16PBinary]);
4.6.4 批处理点(Batched Points)

这个示例中,前两个点的 batchId 为 0,后两个点的 batchId 为 1。注意,Batch Table 只包含两个名字:

javascript 复制代码
var featureTableJSON = { 
  POINTS_LENGTH: 4, 
  BATCH_LENGTH: 2, 
  POSITION: { 
    byteOffset: 0 
  }, 
  BATCH_ID: { 
    byteOffset: 48, 
    componentType: "UNSIGNED_BYTE" 
  } 
}; 

var positionBinary = new Buffer(new Float32Array([ 
  0.0, 0.0, 0.0, 
  1.0, 0.0, 0.0, 
  0.0, 0.0, 1.0, 
  1.0, 0.0, 1.0 
]).buffer); 

var batchIdBinary = new Buffer(new Uint8Array([ 
  0, 
  0, 
  1, 
  1 
]).buffer); 

var featureTableBinary = Buffer.concat([positionBinary, batchIdBinary]); 

var batchTableJSON = { 
  names: ['object1', 'object2'] 
};
4.6.5 每点属性(Per-point Properties)

在这个示例中,四个点将有元数据存储在 Batch Table 的 JSON 和二进制部分:

javascript 复制代码
var featureTableJSON = { 
  POINTS_LENGTH: 4, 
  POSITION: { 
    byteOffset: 0 
  } 
}; 

var featureTableBinary = new Buffer(new Float32Array([ 
  0.0, 0.0, 0.0, 
  1.0, 0.0, 0.0, 
  0.0, 0.0, 1.0, 
  1.0, 0.0, 1.0 
]).buffer); 

var batchTableJSON = { 
  names: ['point1', 'point2', 'point3', 'point4'] 
};

5 批处理表(Batch Table)

批处理表存储的是按 batchId 索引的应用程序特定元数据,可用于声明式样式和应用程序特定用例(如填充 UI 或发出 REST API 请求)。

  • 如果定义了 BATCH_ID 语义,批处理表存储每个 batchId 的元数据,批处理表的数组长度将等于 BATCH_LENGTH
  • 如果未定义 BATCH_ID 语义,批处理表存储每个点的元数据,批处理表的数组长度将等于 POINTS_LENGTH

6 文件扩展名和 MIME 类型(File Extension and MIME Type)

点云瓦片使用 .pnts 扩展名和 application/octet-stream MIME 类型。文件扩展名是可选的,有效的实现可以忽略扩展名,直接通过头部的魔术字段来识别内容格式。

7 实现示例(Implementation Example)

这部分内容是非规范性的,实际代码实现可以参考 Cesium 的 PointCloud3DModelTileContent.js 文件,它提供了 3D Tiles 的头部读取代码。
TilesBuilder 工具可转换部分三维格式

8 属性参考(Property Reference)

8.1 点云特征表(Point Cloud Feature Table)

点云特征表定义了用于描述瓦片中点的位置信息和外观属性的语义。其属性包括:

  • POSITION: 定义点的位置(包括量化位置)。
  • POSITION_QUANTIZED: 定义量化点的位置。
  • RGBA, RGB, RGB565: 定义点的颜色。
  • NORMAL, NORMAL_OCT16P: 定义点的法线。
  • BATCH_ID: 用于批处理每个点的 batchId
  • POINTS_LENGTH: 定义点的数量。
  • RTC_CENTER: 定义渲染中心。
  • QUANTIZED_VOLUME_OFFSETQUANTIZED_VOLUME_SCALE: 定义量化体积的偏移和缩放因子。

这些属性可以直接在 JSON 中定义,或者通过 BinaryBodyReference 对象引用二进制体中的对应部分。

8.2 二进制体引用(BinaryBodyReference)

BinaryBodyReference 定义了一个对象,该对象指向二进制体中存储特定属性值的部分,包含 byteOffset 属性,指定从二进制缓冲区的偏移量。

属性:
  • byteOffset (类型: number, 必需):

    指定在二进制体中的偏移量(单位为字节)。

    • 要求: 必需
    • 最小值: 大于等于 0
  • 其他属性 :

    可以根据需要在 BinaryBodyReference 中添加其他自定义属性,但 byteOffset 是必需的。

8.2.1 BinaryBodyReference.byteOffset

byteOffsetBinaryBodyReference 中的一个关键属性,定义了该属性值在二进制缓冲区中的起始位置。它指定了从二进制数据开始位置的偏移量,通常是字节单位。

  • 类型: number
  • 要求: 必需
  • 最小值: >= 0

8.3 GlobalPropertyCartesian3

GlobalPropertyCartesian3 是一个对象,它定义了适用于所有特征的全局 3 组件数值属性。 通常,这些数值表示与三维坐标相关的全局属性,如位置、方向等。

8.4 GlobalPropertyCartesian4

GlobalPropertyCartesian4 是一个对象,它定义了适用于所有特征的全局 4 组件数值属性。 这通常用于表示四维数值属性,例如带有时间或颜色信息的 3D 坐标。

8.5 GlobalPropertyScalar

GlobalPropertyScalar 是一个对象,它定义了适用于所有特征的全局标量数值属性。 这种类型的属性用于表示单一的数值,如某些全局设置或常量参数。

8.6 Property

Property 是一个用户定义的属性,指定每个特征的应用程序特定元数据。

这些属性的值可以直接在 JSON 中定义,也可以通过BinaryBodyReference 引用二进制体中的某个部分。 如果 Property 是数组类型,它允许存储每个特征的多个值。

如果你有兴趣了解如何实现或使用点云格式,或者在具体的项目中遇到技术难题,可以随时向我咨询!

相关推荐
lrlianmengba1 小时前
推荐一款3D建模软件:Agisoft Metashape Pro
3d
智方科技1 小时前
如何编译 Cesium 源码
3d·智慧城市·cesium·tilesbuilder
sduerfh14 小时前
pytorch3d导入maya相机位姿踩坑
pytorch·3d·maya
erxij16 小时前
【游戏引擎之路】登神长阶(十三)——Vulkan教程:讲个笑话:离开舒适区
c++·经验分享·游戏·3d·游戏引擎
一名技术极客1 天前
Three.js 搭建3D隧道监测
开发语言·javascript·3d
mirrornan2 天前
产品如何3D建模?如何根据使用场景选购3D扫描仪?
科技·3d·3d建模·3d模型·三维扫描
兔老大的胡萝卜2 天前
关于 3D Engine Design for Virtual Globes(三维数字地球引擎设计)
人工智能·3d
深蓝学院2 天前
无需姿态,即刻重建!NoPoSplat,重新定义3DGS的重建Pipeline
3d
mirrornan2 天前
3D看车如何实现?有哪些功能特点和优势?
3d·3d模型·3d交互展示·3d看车