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 头部中的。
- 如果同时定义了
POSITION
和POSITION_QUANTIZED
,则使用高精度的POSITION
。 - 如果同时定义了
NORMAL
和NORMAL_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_OFFSET
和 QUANTIZED_VOLUME_SCALE
中。如果没有定义这些全局语义,就无法使用 POSITION_QUANTIZED
。
量化位置到局部空间的映射公式如下:
POSITION = POSITION_QUANTIZED * QUANTIZED_VOLUME_SCALE / 65535.0 + QUANTIZED_VOLUME_OFFSET
4.3 点的颜色(Point Colors)
如果定义了多个颜色语义,则颜色的优先顺序为:RGBA
> RGB
> RGB565
> CONSTANT_RGBA
。例如,如果一个瓦片的特征表包含 RGBA
和 CONSTANT_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_BYTE
、UNSIGNED_SHORT
或 UNSIGNED_INT
的数据类型。如果没有指定 componentType
,则默认为 UNSIGNED_SHORT
。
全局语义 BATCH_LENGTH
定义了唯一的 BATCH_ID
数量,类似于Batched 3D Model
4.6 示例(Examples)
4.6.1 只有位置(Positions Only)
这是一个简单的示例,包含四个点,它们位于单位长度正方形的四个角上:
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)
以下示例包含四个点(分别为红色、绿色、蓝色和黄色),它们的位置是相对于中心定义的:
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 单位:
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 只包含两个名字:
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 和二进制部分:
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_OFFSET
和QUANTIZED_VOLUME_SCALE
: 定义量化体积的偏移和缩放因子。
这些属性可以直接在 JSON 中定义,或者通过 BinaryBodyReference
对象引用二进制体中的对应部分。
8.2 二进制体引用(BinaryBodyReference)
BinaryBodyReference
定义了一个对象,该对象指向二进制体中存储特定属性值的部分,包含 byteOffset
属性,指定从二进制缓冲区的偏移量。
属性:
-
byteOffset (类型: number, 必需):
指定在二进制体中的偏移量(单位为字节)。- 要求: 必需
- 最小值: 大于等于 0
-
其他属性:
可以根据需要在BinaryBodyReference
中添加其他自定义属性,但byteOffset
是必需的。
8.2.1 BinaryBodyReference.byteOffset
byteOffset
是 BinaryBodyReference
中的一个关键属性,定义了该属性值在二进制缓冲区中的起始位置。它指定了从二进制数据开始位置的偏移量,通常是字节单位。
- 类型: number
- 要求: 必需
- 最小值: >= 0
8.3 GlobalPropertyCartesian3
GlobalPropertyCartesian3
是一个对象,它定义了适用于所有特征的全局 3 组件数值属性。 通常,这些数值表示与三维坐标相关的全局属性,如位置、方向等。
8.4 GlobalPropertyCartesian4
GlobalPropertyCartesian4
是一个对象,它定义了适用于所有特征的全局 4 组件数值属性。 这通常用于表示四维数值属性,例如带有时间或颜色信息的 3D 坐标。
8.5 GlobalPropertyScalar
GlobalPropertyScalar
是一个对象,它定义了适用于所有特征的全局标量数值属性。 这种类型的属性用于表示单一的数值,如某些全局设置或常量参数。
8.6 Property
Property
是一个用户定义的属性,指定每个特征的应用程序特定元数据。
这些属性的值可以直接在 JSON 中定义,也可以通过BinaryBodyReference
引用二进制体中的某个部分。 如果 Property
是数组类型,它允许存储每个特征的多个值。
如果你有兴趣了解如何实现或使用点云格式,或者在具体的项目中遇到技术难题,可以随时向我咨询!