NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 - 3D道路快速建模
本文档描述了 3D Tiles 1.1规范,这是一种用于流式传输大量异构 3D 地理空间数据集的开放标准。本规范中文版有BimAnt翻译整理。
1、3D Tiles介绍
3D Tiles 专为流式传输和渲染大量 3D 地理空间内容而设计,例如摄影测量、3D 建筑、BIM/CAD、实例化特征和点云。它定义了一个分层数据结构和一组提供可渲染内容的图块格式。3D Tiles 没有为内容的可视化定义明确的规则;你可以用自己认为合适的方式可视化 3D Tiles 数据。
在 3D Tiles 中,tileset是在空间数据结构树中组织的一组图块。一个tileset由至少一个tileset JSON文件描述,该文件包含tileset元数据和一个tile对象树,每个tile对象都可以引用可渲染的内容。
glTF 2.0是 3D Tiles的基本tile格式。glTF 是一个开放式规范,专为高效传输和加载 3D 内容而设计。glTF 资产包括单个tile的几何和纹理信息,并且可以扩展为包括元数据、模型实例化和压缩处理。glTF 可用于各种 3D 内容,包括:
- 异构 3D 模型。例如带纹理的地形和表面、3D 建筑外部和内部、海量模型
- 3D 模型实例。例如树木、风车、螺栓
- 海量点云
有关使用glft作为Tile内容的详细信息,请参阅glTF Tile Format。
Tiles 也可能引用下面列出的旧版 3D Tiles 1.0 格式。这些格式在 3D Tiles 1.1 中已弃用,可能会在未来版本的 3D Tiles 中删除。
表 1. 传统Tile格式和常见用途
传统格式 | 用途 |
---|---|
异构 3D 模型 | |
3D 模型实例 | |
海量点数 | |
将不同格式的Tile连接到一个Tile中 |
Tile的内容是Tile格式的单个实例。一个 tile 可能包含多个内容。
内容引用了一组特征,例如表示建筑物或树木的 3D 模型,或点云中的点。每个特征都具有位置和外观属性以及其他特定于应用程序的属性。客户端可以选择在运行时根据特征属性以进行内容过滤、可视化或分析。
Tile以树的形式组织,其中结合了层次细节层次 (HLOD) 的概念,以优化空间数据的渲染。每个 tile 都有一个bounding volume,一个定义完全包围其内容的空间范围的对象。Tile树具有空间连贯性;子图块的内容完全在父图块的包围体内。
Tile集合可以使用类似于2D栅格和矢量瓦片方案,如 Web 地图瓦片服务 (WMTS) 或 XYZ 方案,它们以多个细节级别(或缩放级别)提供预定义瓦片。然而,由于Tile集的内容通常是不均匀的,或者可能不容易在二维中组织,因此树可以是任何具有空间一致性的空间数据结构,包括 kd 树、四叉树、八叉树和栅格。隐式平铺定义了四叉树和八叉树的简洁表示。
可以在Tile集中以多个粒度提供特定于应用程序的元数据。元数据可能与高级实体(如Tile集、Tile、内容或特征)相关联,或者与单个顶点和纹素相关联。元数据符合3D 元数据规范所描述的明确定义的类型系统,可以使用特定于应用程序或特定领域的语义进行扩展。
可以选择将3D Tiles Style或style应用到Tile集。样式定义要评估的表达式,这些表达式修改每个特征的显示方式。
2、文件扩展名和媒体类型
3D Tiles 使用以下文件扩展名和媒体类型。
- Tileset 文件应使用
.json
扩展名和application/json
媒体类型。 - Tile内容文件应使用特定于其Tile格式规范的文件扩展名和媒体类型。
- 元数据架构文件应使用
.json
扩展名和application/json
媒体类型。 - Tileset 样式文件应该使用
.json
扩展名和application/json
媒体类型。 - JSON 子树文件应使用
.json
扩展名和application/json
媒体类型。 - 二进制subtree文件应使用
.subtree
扩展名和application/octet-stream
媒体类型。 - 表示二进制缓冲区的文件应使用
.bin
扩展名和application/octet-stream
媒体类型。
显式文件扩展名是可选的。有效的3D Tiles解析器实现可能会忽略扩展名并通过文件头中的magic
字段来识别内容的格式。
3、JSON编码
3D Tiles 对 JSON 格式和编码有以下限制。
- JSON 应使用不带 BOM 的 UTF-8 编码。
- 本规范中定义的所有字符串(属性名称、枚举)仅使用 ASCII 字符集,并且应写为纯文本,没有 JSON 转义。
- 在 JSON 中显示为属性值的非 ASCII 字符可能会被转义。
- JSON 对象中的名称(键)应是唯一的,即不允许重复键。
- 某些属性在模式中定义为整数。如RFC 8259 第 6 节中定义的那样,此类值可以存储为小数部分为零的小数或使用指数表示法。
4、URI
3D Tiles 使用 URI 来引用 tile 内容。这些 URI 可能指向相对外部引用 (RFC3986),或者是在 JSON 中嵌入资源的数据 URI。嵌入式资源使用“数据”URL 方案 (RFC2397)。
当 URI 是相对引用时,以引用的 tileset JSON 文件作为基路径。
客户端实现需要支持相关的外部引用和嵌入式资源。可选地,客户端实现可以支持其他方案(例如http://
)。所有 URI 都应有效且可解析。
5、单位
所有直线距离的单位都是米。
所有角度都以弧度为单位。
6、坐标参考系统 (CRS)
3D Tiles 使用右手笛卡尔坐标系;也就是说,x和y的叉积产生z。3D Tiles 将z轴定义为局部笛卡尔坐标系的向上。tileset的全局坐标系通常位于WGS 84地心固定 (ECEF) 参考系 ( EPSG 4978 ) 中,但它不是必须的,例如,发电厂可以在其本地完全定义用于没有地理空间上下文的建模工具的坐标系。
一个tileset的 CRS 可以明确定义为tileset元数据的一部分。Tileset的元数据可以包含一个具有TILESET_CRS_GEOCENTRIC
语义的属性,它是一个表示 EPSG 大地参数数据集标识符的字符串。
可以应用额外的Tile变换来将Tile的局部坐标系转换为父Tile的坐标系。
区域边界体积使用地理坐标系(纬度、经度、高度)指定边界,具体而言,EPSG 4979。假设参考椭球与tileset的参考椭球相同。
7、3D Tiles基础概念
7.1 Tile
由用于确定是否渲染Tile的元数据、对可渲染内容的引用以及任何子Tile的数组组成。
7.2 Tile Content
Tile可以与可渲染内容相关联。一个 tile 可以有一个tile.content
对象,也可以有多个内容对象存储在一个tile.contents
数组中。后者允许灵活的tileset结构:例如,单个tile中可能包含相同几何数据的多个表示,例如网格和点云表示。
每个内容对象的content.uri
是指在Tile格式规范中定义的某种Tile内容,或指向另一个tileset JSON (请参阅外部tileset)。
content.group
属性将内容分配给一个组。可以将不同tile的内容或单个tile的内容分配给组以便对内容进行分类。此外,每个组都可以与Metadata相关联。
每个内容都可以与一个包围体相关联。虽然tile.boundingVolume
是一个包围tile的所有内容的包围体,但单个content.boundingVolume
都是紧密配合的包围体,仅包围各自的内容。
7.3 Geometric error:几何误差
Tile被结构化为包含层次详细级别(HLOD) 的树,因此在运行时客户端实现将需要确定某个tile是否足够详细以进行渲染,以及tile的内容是否应由更高分辨率的子tile继续细化。3D Tiles客户端实现将考虑最大允许的屏幕空间误差(SSE),即以像素为单位测量的误差。
Tile的几何误差定义了这个tile的选择度量。它的值是一个非负数,用于指定tile对其源几何图形的简化表示的误差(以米为单位)。一般情况下,根tile的几何误差最大,每个后续级别的子tile的几何误差都小于其父tile,叶级tile的几何误差为或接近 0。
在客户端实现中,几何误差与其他屏幕空间指标(例如,从tile到相机的距离、屏幕大小和分辨率)一起使用,以计算在渲染此tile而其子项未渲染时引入的 SSE。如果引入的 SSE 超过了允许的最大值,则对 tile 进行细化,并考虑对其子项进行渲染。
几何误差是基于点密度、网格或纹理抽取等度量或特定于该tileset的其他因素制定的。一般来说,较高的几何误差意味着将更加积极地细化tile,并且将更快地加载和渲染子tile。
7.4 Refine:细化
Refine决定当选中较高分辨率的子tile时,如何处理较低分辨率的父tile。允许的细化类型是替换 ( "REPLACE"
) 和添加 ( "ADD"
)。如果Tile的细化属性设置为 REPLACE
,则子tile将代替父tile进行渲染,即不再渲染父tile。如果Tile的细化属性设置为 ADD
,则除了父tile之外还渲染子tile。
一个tileset可以仅使用替换细化、添加细化或者这两者任何组合。
Tileset的根tile必修设置细化类型;对于所有其他tile,细化属性是可选的。省略时,tile继承其父项的细化类型。
如果一个 tile 使用替换细化,当细化时它会渲染其子元素来代替它自己:
如果Tile使用添加细化,则在细化时它会同时渲染自身及其子级:
7.5 Bounding Volumes:包围体
包围体定义了包围tile或tile内容的空间范围。为了支持各种数据集的紧密拟合体积,例如规则划分的地形、未与纬度或经度线对齐的城市或任意点云,包围体类型包括有向边界框、边界球体和由最小/最大纬度/经度/高度定义的地理区域。
boundingVolume.region
属性是一个由六个数字组成的数组,这些数字用纬度、经度和高度坐标定义边界地理区域,顺序为[west, south, east, north, minimum height, maximum height]
。纬度和经度位于EPSG 4979中定义的 WGS 84 基准中,并以弧度为单位。高度以米为单位,高于(或低于)WGS 84 椭球。例如:
"boundingVolume": {
"region": [
-1.3197004795898053,
0.6988582109,
-1.3196595204101946,
0.6988897891,
0,
20
]
}
表现如下:
boundingVolume.box
属性是一个由 12 个数字组成的数组,用于在 z 轴向上的右手 3 轴 (x, y, z) 笛卡尔坐标系中定义定向边界框。前三个元素定义框中心的 x、y 和 z 值。接下来的三个元素(索引为 3、4 和 5)定义x轴方向和半长。接下来的三个元素(索引 6、7 和 8)定义y轴方向和半长。最后三个元素(索引 9、10 和 11)定义z轴方向和半长。例如:
"boundingVolume": {
"box": [
0, 0, 10,
100, 0, 0,
0, 100, 0,
0, 0, 10
]
}
表现如下:
boundingVolume.sphere
属性是一个由四个数字组成的数组,用于定义一个边界球。前三个元素在 z 轴向上的右手 3 轴 (x, y, z) 笛卡尔坐标系中定义球体中心的 x、y 和 z值。最后一个元素(索引为 3)定义了以米为单位的半径。例如:
"boundingVolume": {
"sphere": [
0,
0,
10,
141.4214
]
}
表现如下:
可以通过tile.boundingVolume
属性为每个tile指定包围体。此外,可以单独为每个tile内容指定包围体。content.boundingVolume
可能是一个更紧密的包围体,这可以实现紧密的视锥体剔除,排除渲染不在潜在视野范围内的任何内容。当它没有被定义时,tile的边界体积仍然用于剔除(参见Grids)。
下面的屏幕截图显示了 Canary Wharf 根tile的包围体— tile.boundingVolume
— 以红色显示,包围了tileset的整个区域;content.boundingVolume
以蓝色显示,仅包含根tile中的四个特征(模型):
通过扩展可以支持其他包围体类型,参见:3DTILES_bounding_volume_S2 。
7.6 ViewerRequestVolume:查看者请求体
viewerRequestVolume
可用于组合异构数据集,并可与外部tileset组合。
以下示例在建筑物内有一个点云。点云tile的boundingVolume
是一个半径为 1.25
的球体。它还有一个更大的球体,即半径15
为viewerRequestVolume
. 由于geometricError
是零,所以当查看器位于由 viewerRequestVolume
定义的大球内时,总是会渲染(并且最初请求)点云tile的内容:
{
"children": [{
"transform": [
4.843178171884396, 1.2424271388626869, 0, 0,
-0.7993325488216595, 3.1159251367235608, 3.8278032889280675, 0,
0.9511533376784163, -3.7077466670407433, 3.2168186118075526, 0,
1215001.7612985559, -4736269.697480114, 4081650.708604793, 1
],
"boundingVolume": {
"box": [
0, 0, 6.701,
3.738, 0, 0,
0, 3.72, 0,
0, 0, 13.402
]
},
"geometricError": 32,
"content": {
"uri": "building.glb"
}
}, {
"transform": [
0.968635634376879, 0.24848542777253732, 0, 0,
-0.15986650990768783, 0.6231850279035362, 0.7655606573007809, 0,
0.19023066741520941, -0.7415493329385225, 0.6433637229384295, 0,
1215002.0371330238, -4736270.772726648, 4081651.6414821907, 1
],
"viewerRequestVolume": {
"sphere": [0, 0, 0, 15]
},
"boundingVolume": {
"sphere": [0, 0, 0, 1.25]
},
"geometricError": 0,
"content": {
"uri": "points.glb"
}
}]
}
有关请求体的更多信息,请参阅示例 tileset。
7.7 变换
为了支持局部坐标系——例如,一个城市tileset中的建筑tileset可以在它自己的坐标系中定义,并且建筑物内的点云tileset可以再次在它自己的坐标系中定义——每个tile都有一个可选transform
属性。
transform
属性是一个 4x4 仿射变换矩阵,以列优先顺序存储,它从tile的本地坐标系转换到父tile的坐标系 - 或在根tile的情况下为tileset的坐标系。
transform
属性适用于tile.content
:
- 每个特征的位置。
- 当使用 scale 时,每个特征的法线都应通过
transform
逆转置的左上角 3x3 矩阵进行转换以说明正确的向量转换。 content.boundingVolume
, 除非content.boundingVolume.region
在 EPSG:4979 坐标中明确定义。
transform
属性适用于tile.boundingVolume
, 除非tile.boundingVolume.region
在 EPSG:4979 坐标中明确定义。
transform
属性适用于tile.viewerRequestVolume
, 除非tile.viewerRequestVolume.region
在 EPSG:4979 坐标中明确定义。
transform
属性按矩阵中的最大比例因子缩放geometricError
。
transform
未定义时,默认为单位矩阵:
[ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]
从每个tile的局部坐标系到tileset的全局坐标系的转换是通过tileset的自上而下遍历以及通过将子级transform
与其父级后乘来计算的,transform
就像计算机图形中的传统场景图或节点层次结构一样。
glTF 定义了自己的节点层次结构并使用y向上坐标系。tile.transform
在解析这些转换后,将应用特定于切片格式和属性的任何转换。
首先,根据glTF 规范应用 glTF 节点层次结构变换。
接下来,为了与 3D Tiles 的z -up 坐标系统保持一致,glTF 应在运行时从y -up 转换为z -up。这是通过将模型绕x轴旋转 π/2 弧度来完成的。等效地,应用以下矩阵变换(此处显示为行优先):
[
1.0, 0.0, 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0
]
更广泛地说,转换的顺序是:
当处理固有z向上的源数据时,例如 WGS 84 坐标或局部z向上坐标系中的数据,常见的工作流程是:
- 网格数据,包括位置和法线,不会被修改 - 它们保持z向上。
- 根节点矩阵指定列优先的z向上到y向上的变换。这会将源数据转换为 glTF 所需的y向上坐标系。
- 在运行时,glTF使用上面的矩阵从y -up转换回z -up。实际上,变换抵消了。
示例 glTF 根节点:
"nodes": [
{
"matrix": [1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1],
"mesh": 0,
"name": "rootNode"
}
]
对于tileset的计算转换(在上面代码中的transformToRoot
)的示例,请考虑:
每个tile的计算变换为:
TO
:[T0]
T1
:[T0][T1]
T2
:[T0][T2]
T3
:[T0][T1][T3]
T4
:[T0][T1][T4]
考虑到glTF y -up到z -up变换和glTF 变换的完整计算变换是
TO
:[T0]
T1
:[T0][T1]
T2
:[T0][T2][glTF y-up to z-up][glTF transform]
T3
:[T0][T1][T3][glTF y-up to z-up][glTF transform]
T4
:[T0][T1][T4][glTF y-up to z-up][glTF transform]
下面的 JavaScript 代码展示了如何使用 Cesium 的Matrix4和Matrix3类型来计算上述变换。
function computeTransforms(tileset) {
const root = tileset.root;
const transformToRoot = defined(root.transform) ? Matrix4.fromArray(root.transform) : Matrix4.IDENTITY;
computeTransform(root, transformToRoot);
}
function computeTransform(tile, transformToRoot) {
// Apply 4x4 transformToRoot to this tile's positions and bounding volumes
let normalTransform = Matrix4.getRotation(transformToRoot, new Matrix4());
normalTransform = Matrix3.inverseTranspose(normalTransform, normalTransform);
// Apply 3x3 normalTransform to this tile's normals
const children = tile.children;
if (defined(children)) {
const length = children.length;
for (let i = 0; i < length; ++i) {
const child = children[i];
let childToRoot = defined(child.transform) ? Matrix4.fromArray(child.transform) : Matrix4.clone(Matrix4.IDENTITY);
childToRoot = Matrix4.multiplyTransformation(transformToRoot, childToRoot, childToRoot);
computeTransform(child, childToRoot);
}
}
}
7.8 Tile JSON
Tile的 JSON 对象包含以下属性:
以下示例显示了一个非叶子tile:
{
"boundingVolume": {
"region": [
-1.2419052957251926,
0.7395016240301894,
-1.2415404171917719,
0.7396563300150859,
0,
20.4
]
},
"geometricError": 43.88464075650763,
"refine" : "ADD",
"content": {
"boundingVolume": {
"region": [
-1.2418882438584018,
0.7395016240301894,
-1.2415422846940714,
0.7396461198389616,
0,
19.4
]
},
"uri": "2/0/0.glb"
},
"children": [...]
}
boundingVolume
定义了一个包含tile的体,并用于确定在运行时要渲染哪些tile。上面的示例使用了一个region
包围体,但也可以使用其他包围体,例如box
或sphere
。
geometricError
属性是一个非负数,用于定义在渲染此tile且其子tile未渲染时引入的误差(以米为单位)。在运行时,几何误差用于计算屏幕空间误差(SSE),即以像素为单位测量的误差。SSE 确定一个tile对于当前视图是否足够详细,或者是否应考虑其子项,请参阅几何错误。
可选的 viewerRequestVolume
属性(上面未显示)定义了一个包围体,使用与boundingVolume
相同的模式,在请求tile的内容之前以及在基于 geometricError
优化tile之前,查看器应位于其中。请参阅查看器请求体部分。
refine
属性是"REPLACE"
用于替换细化或"ADD"
附加细化的字符串,请参阅Refinement。tileset的根tile需要它;对于所有其他tile,它是可选的。一个tileset可以使用添加和替换细化的任意组合。当refine
属性被省略时,它是从父 tile 继承的。
content
属性是描述tile内容的对象。content.uri
不需要文件扩展名。内容的tile格式可以通过文件头中的magic
字段来标识,或者如果内容是 JSON,则作为外部tileset。
content.boundingVolume
属性定义了一个类似于顶级tile.boundingVolume
属性的可选包围体。但与顶级boundingVolume
属性不同的是,content.boundingVolume
是一个紧密贴合的包围体,仅包含tile的内容。
也可以为一个 tile 定义多个内容:contents
属性(上面未显示)是一个包含一个或多个内容的数组。contents
与content
是互斥的。当tile具有单一内容时,它应该 使用content
向后兼容仅支持 3D Tiles 1.0 的引擎。多个内容允许对 tile 内容进行不同的表示——例如,一个作为网格,一个作为点云:
还可以使用 content.group
属性将内容分组:
{
"root": {
"refine": "ADD",
"geometricError": 0.0,
"boundingVolume": {
"region": [-1.707, 0.543, -1.706, 0.544, 203.895, 253.113]
},
"contents": [
{
"uri": "buildings.glb",
"group": 0
},
{
"uri": "trees.glb",
"group": 1
},
{
"uri": "cars.glb",
"group": 2
}
]
}
}
这些组可以与组元数据相关联:content.group
属性的值是数组的索引,该数组groups
在tileset的顶级数组中定义。该数组的每个元素都是元数据实体,如元数据部分中所定义。这允许应用程序根据内容所属的组执行样式设置或过滤:
可选的 transform
属性(上面未显示)定义了一个 4x4 仿射变换矩阵,该矩阵可以变换瓷砖的content
、boundingVolume
和viewerRequestVolume
,如Tile变换部分所述。
可选的 implicitTiling
属性(上面未显示)定义如何细分Tile以及定位内容资源的位置。
children
属性是定义子tile的对象数组。每个子 tile 的内容完全被其父 tile 的boundingVolume
包围,并且通常geometricError
小于其父 tile 的geometricError
。对于叶子tile,该数组的长度为零,并且children
可能未定义。请参阅下面的Tileset JSON部分。
完整的 JSON 模式可以参考 tile.schema.json
。
7.9 Tileset JSON
3D Tiles 使用一个主tileset JSON 文件作为定义图块集的入口点。条目和外部tileset JSON 文件都不需要遵循特定的命名约定。
这是用于 Canary Wharf 的 tileset JSON 的一个子集:
{
"asset" : {
"version": "1.1",
"tilesetVersion": "e575c6f1-a45b-420a-b172-6449fa6e0a59",
},
"properties": {
"Height": {
"minimum": 1,
"maximum": 241.6
}
},
"geometricError": 494.50961650991815,
"root": {
"boundingVolume": {
"region": [
-0.0005682966577418737,
0.8987233516605286,
0.00011646582098558159,
0.8990603398325034,
0,
241.6
]
},
"geometricError": 268.37878244706053,
"refine": "ADD",
"content": {
"uri": "0/0/0.glb",
"boundingVolume": {
"region": [
-0.0004001690908972599,
0.8988700116775743,
0.00010096729722787196,
0.8989625664878067,
0,
241.6
]
}
},
"children": [...]
}
}
Tileset JSON 有四个顶级属性:asset
、properties
、geometricError
和root
。
asset
是一个对象,包含有关整个tileset的元数据。该asset.version
属性是一个字符串,它定义了 3D Tiles 版本,它指定了tileset的 JSON 模式和tile格式的基本集。该tilesetVersion
属性是一个可选字符串,它定义了一个特定于应用程序的tileset版本,例如,当现有tileset被更新时。
可以在请求内容时用tilesetVersion
作为查询参数,以避免使用缓存中的过时内容。
properties
是一个对象,其中包含tileset中每个特征属性的对象。tileset JSON 片段用于 3D 建筑物,因此每个图块都有建筑模型,并且每个建筑模型都有一个Height
属性(请参阅批处理表)。每个对象properties
的名称与每个特征属性的名称相匹配,其值定义了其值minimum
和maximum
数值,例如,用于创建样式的颜色渐变。
geometricError
是一个非负数,它定义了确定是否渲染tileset的误差,以米为单位。在运行时,几何误差用于计算屏幕空间误差(SSE),即以像素为单位测量的误差。如果 SSE 没有超过要求的最小值,则不应渲染图块集,并且不应考虑渲染其任何图块,请参阅几何错误。
root
是一个对象,它使用上一节中描述的 tile JSON 定义根 tile 。root.geometricError
与tileset的顶级不一样geometricError
。tileset的geometricError
在运行时用于确定根tile呈现的 SSE;root.geometricError
在运行时用于确定渲染根 tile 的子项的 SSE。
7.10 外部tileset
要创建一棵树,一个tile的content.uri
可以指向一个外部tileset。例如,这使得将每个城市存储在一个tileset 中,然后拥有一个全局tileset。
当一个tile指向一个外部tileset时,瓦片:
- 不能有子tile;
tile.children
应省略 - 不能用于创建循环,例如,通过指向包含该tile的同一tileset文件或通过指向另一个tileset文件,然后再指向包含该tile的初始文件。
- 将同时应用tile和跟tile的
transform
。例如,在以下引用外部tileset的tileset中,T3
计算的变换为[T0][T1][T2][T3]
.
如果外部tileset定义了asset.tilesetVersion
,这将覆盖父tileset的值。如果外部tileset没有定义asset.tilesetVersion
,则该值继承自父tileset(如果已定义)。
7.11 包围体空间相干性
如上所述,树具有空间相干性;每个tile都有一个完全包围其内容的包围体,子tile的内容完全在父包围体内。这并不意味着子tile的包围体完全在其父包围体之内。例如 地形图块的包围体:
四个子tile的包围体。子tile的内容完全在父tile的包围体内,但子tile的包围体则不在,因为它们不是紧密配合的:
7.12 空间数据结构
3D Tiles 结合了层次细节层次 (HLOD) 的概念,以实现空间数据的最佳渲染。tileset由一棵树组成,树root
由其tile定义并递归地定义,这些children
可以由不同类型的空间数据结构组织。
运行时引擎是通用的,将渲染由tileset定义的任何树。可以使用切片格式和细化方法的任何组合,从而灵活地支持异构数据集,请参阅细化。
tileset可以使用类似于栅格和矢量瓦片方案(如 Web 地图瓦片服务 (WMTS) 或 XYZ 方案)的 2D 空间瓦片方案,它们以多个细节级别(或缩放级别)提供预定义tile。然而,由于tileset的内容通常是不均匀的,或者可能不容易在二维中组织,因此其他空间数据结构可能更优化。
下面简要介绍了 3D Tiles 如何表示各种空间数据结构。
四叉树
当每个tile具有四个均匀细分的子节点(例如,使用中心纬度和经度)时,将创建四叉树,类似于典型的 2D 地理空间图块方案。可以省略空子tile。
3D Tiles 支持四叉树变化,例如非均匀细分和紧密包围体(与边界相反,例如,父 tile 的完整 25%,这对于稀疏数据集来说是浪费的)。
例如,这里是 Canary Wharf 的根tile及其子tile。注意左下角,包围体不包括左侧没有建筑物出现的水:
3D Tiles 还支持其他四叉树变体,例如松散四叉树,其中子tile重叠但仍保留空间连贯性,即父tile完全包围其所有子图块。此方法可用于避免跨tile拆分特征,例如 3D 模型。
下面,绿色建筑在左子tile上,紫色建筑在右tile上。请注意,tile重叠,因此中心的两座绿色和一座紫色建筑不会分开。
KD树
当每个 tile 有两个子节点被平行于x、y或z轴(或纬度、经度、高度)的分割平面分开时,就会创建 kd 树。分裂轴通常随着树的层级增加而循环旋转,并且可以使用中值分裂、表面积启发法或其他方法来选择分裂平面。
请注意,kd 树不像典型的 2D 地理空间切片方案那样具有统一的细分,因此可以为稀疏和非均匀分布的数据集创建更平衡的树。
3D Tiles 支持 kd 树的变化,例如多路 kd 树,其中,在树的每一片叶子上,沿轴有多个分裂。每个tile不是有两个孩子,而是有n
孩子。
八叉树
八叉树通过使用三个正交分割平面来扩展四叉树,将一个瓦片细分为八个子级。与四叉树一样,3D Tiles 允许对八叉树进行变化,例如非均匀细分、紧密边界体积和重叠子代。
栅格
3D Tiles 通过支持任意数量的子tile来实现统一、非统一和重叠的栅格。例如,这是剑桥非均匀重叠栅格的俯视图:
3D Tiles 利用空的tiles:那些有边界体积但没有内容的tiles。由于content
不需要定义tile的属性,因此可以使用空的非叶tile来加速具有层次剔除的非均匀网格。这实质上创建了一个没有层次详细级别 (HLOD) 的四叉树或八叉树。
7.13 Implicit Tilling:隐式平铺
可以明确定义包围体层次结构 (如前所示),这可以实现多种空间数据结构。可以隐式定义某些常见的数据结构,例如四叉树和八叉树,而无需为每个tile提供边界体积。这种常规模式允许基于切片坐标随机访问切片,从而实现加速的空间查询、新的遍历算法和切片内容的有效更新,以及其他用例。
为了支持稀疏数据集,可用性数据确定存在哪些tile。为了支持海量数据集,可用性被划分为固定大小的子树。子树可以存储可用tile和内容的元数据。
可以将implicitTiling
对象添加到tileset JSON 中的任何tile。该对象定义如何细分tile以及定位内容资源的位置。它可以添加到多个tile中以创建更复杂的细分方案。
以下示例显示了在根tile上定义的四叉树,模板 URI 指向内容和子树文件。
{
"root": {
"boundingVolume": {
"region": [-1.318, 0.697, -1.319, 0.698, 0, 20]
},
"refine": "REPLACE",
"geometricError": 5000,
"content": {
"uri": "content/{level}/{x}/{y}.glb"
},
"implicitTiling": {
"subdivisionScheme": "QUADTREE",
"availableLevels": 21,
"subtreeLevels": 7,
"subtrees": {
"uri": "subtrees/{level}/{x}/{y}.json"
}
}
}
}
7.14 Metadata:元数据
可以在tileset中以多个粒度提供特定于应用程序的元数据。元数据可能与高级实体(如tileset、tile、内容或特征)相关联,或者与单个顶点和纹理相关联。元数据符合由3D 元数据规范描述的明确定义的类型系统,可以使用特定于应用程序或特定领域的语义进行扩展。
元数据支持以下格式的其他用例和功能:
- 查看:在用户界面 (UI) 中显示tileset的应用程序可能允许用户单击或悬停在特定图块或图块内容上,从而在 UI 中显示有关所选实体的信息元数据。
- 集合:tile内容组可用于定义集合(类似于地图图层),以便每个集合可以显示、隐藏或在视觉上设置样式,并在多个tile之间同步效果。
- 结构化数据:元数据支持嵌入式和外部引用的模式,这样tileset的作者可以为公共领域(例如 AEC 或科学数据集)或完全定制的、特定于应用程序的数据(例如特定的视频游戏)定义新的数据模型。
- 优化:每个内容的元数据可能包括具有与性能相关的语义的属性,使引擎能够显着优化遍历和流式算法。
元数据可以与不同粒度级别的tileset元素相关联:
- Tileset - 作为一个整体的 tileset 可能与全局元数据相关联。常见的例子可能包括收集年份、作者详细信息或tileset内容的其他一般上下文。
- Tile- Tile可以单独与更具体的元数据相关联。这可能是tile最后一次更新时的时间戳或瓦片的最大高度,或者是优化遍历算法的空间提示。
- Group- 可以将tile内容组织成组。每个组定义代表一个元数据实体,可以通过将此列表中的索引指定为内容的
group
属性来分配给tile内容。这对于将内容集合作为图层处理非常有用,例如管理可见性或视觉样式。 - Content- tile内容可以单独与更具体的元数据相关联,例如属性字符串列表。
- 具有功能元数据的功能glTF 2.0 资产可以作为tile内容包含在内。该
EXT_structural_metadata
扩展允许将元数据与顶点或纹素相关联。
下图显示了这些实体之间的关系,以及可能与这些实体相关联的元数据示例:
尽管它们是独立定义的,但 3D Tiles 和 glTFEXT_structural_metadata
扩展中的元数据结构都符合3D 元数据规范并建立在 3D 元数据规范的参考实现之上。此处使用的概念和术语指的是 3D 元数据规范,应将其视为定义和要求的规范性参考。本文档在适当的情况下提供了术语的内联定义。
7.15 Metadata Schema:元数据架构
元数据架构定义了元数据的结构。它包含元数据类的定义,这些类是元数据实例的模板,并定义了每个元数据实例具有的一组属性。根据元数据模式参考实现,元数据模式以 JSON 表示形式存储在tileset中。此参考实现包括元数据模式的 JSON 模式定义。
Schemas 可以嵌入到带有该schema
属性的图块集中,或者由该属性在外部引用schemaUri
。多个tileset和 glTF 内容可能会引用相同的模式以避免重复。在外部tileset模式中定义的任何类也应在顶级tileset模式中定义。
具有三个属性“height”、“owners”和“buildingType”的building
类的架构模式。“buildingType”属性将buildingType
枚举作为其数据类型,也在架构中定义。后面的示例展示了实体如何声明它们的类并为其属性提供值。
{
"schema": {
"classes": {
"building": {
"properties": {
"height": {
"type": "SCALAR",
"componentType": "FLOAT32"
},
"owners": {
"type": "STRING",
"array": true,
"description": "Names of owners."
},
"buildingType": {
"type": "ENUM",
"enumType": "buildingType"
}
}
}
},
"enums": {
"buildingType": {
"values": [
{"value": 0, "name": "Residential"},
{"value": 1, "name": "Commercial"},
{"value": 2, "name": "Other"}
]
}
}
}
}
URI 引用的外部架构:
{
"schemaUri": "https://example.com/metadata/buildings/1.0/schema.json"
}
7.16 分配元数据
虽然模式中的类定义了属性的数据类型和含义,但在将元数据分配(即,类被“实例化”)作为 3D Tiles 层次结构中的特定元数据实体之前,属性不会采用特定值。
出现在tileset中的元数据实体的通用结构在 metadataEntity.schema.json
. 每个元数据实体都包含作为其实例的类的名称,以及与该类的属性相对应的属性值字典。分配的每个属性值应由具有相同属性 ID 的类属性定义,其值与类属性的数据类型匹配。一个实体只能为其类的属性的子集提供值,但标记的类属性required: true
不应被省略。
前面介绍的building
类的元数据实体。这样的实体可以通过将其存储为它们各自的metadata
属性来分配给tileset、tile或tile内容:
"metadata": {
"class": "building",
"properties": {
"height": 16.8,
"owners": [ "John Doe", "Jane Doe" ],
"buildingType": "Residential"
}
}
大多数属性值在实体中编码为 JSON。一个值得注意的例外是分配给隐式切片和内容的元数据,以更紧凑的二进制形式存储。
7.17 元数据统计
统计信息提供了关于属性值分布的聚合信息,汇总了tileset中元数据类的所有实例。例如,统计数据可能包括数字属性的最小值/最大值,或特定枚举值的出现次数。
这些汇总统计允许应用程序分析或显示元数据,例如使用声明性样式语言,而无需首先处理完整的数据集来识别颜色渐变和直方图的界限。统计信息是按类提供的,因此应用程序可以基于整个tileset提供样式或上下文,而只需要下载和处理其tile的子集。
统计信息存储在tileset的顶级statistics
对象中。此统计对象的结构在 中定义 statistics.schema.json
。为每个元数据类定义统计信息,包括以下元素:
count
是tileset中出现的类的实体数properties
包含有关在tileset中出现的类的属性的汇总统计信息
属性可能包括以下内置统计信息:
姓名 | 描述 | 类型 |
---|---|---|
| 最低财产价值 | 标量、向量、矩阵 |
| 最大属性值 | … |
| 属性值的算术平均值 | … |
| 物业价值的中位数 | … |
| 属性值的标准差 | … |
| 属性值的方差 | … |
| 属性值的总和 | … |
| 值出现的频率 | 对象,其中键是属性值(对于枚举,枚举名称),值是该属性值的出现次数 |
Tileset 作者可以定义自己的附加统计信息,如下mode
例所示。特定于应用程序的统计信息应使用下划线前缀 (*
) 和小写驼峰式,以保持一致性并避免与未来的内置统计信息冲突。
“building”类的定义,具有三个属性。摘要统计为数字“高度”属性提供了最小值、最大值和(特定于应用程序的)“_mode”。枚举“buildingType”属性由不同枚举值出现的次数汇总。
{
"schema": {
"classes": {
"building": {
"properties": {
"height": {
"type": "SCALAR",
"componentType": "FLOAT32"
},
"owners": {
"type": "STRING",
"array": true
},
"buildingType": {
"type": "ENUM",
"enumType": "buildingType"
}
}
}
},
"enums": {
"buildingType": {
"valueType": "UINT16",
"values": [
{"name": "Residential", "value": 0},
{"name": "Commercial", "value": 1},
{"name": "Hospital", "value": 2},
{"name": "Other", "value": 3}
]
}
}
},
"statistics": {
"classes": {
"building": {
"count": 100000,
"properties": {
"height": {
"minimum": 3.9,
"maximum": 341.7,
"_mode": 5.0
},
"buildingType": {
"occurrences": {
"Residential": 50000,
"Commercial": 40950,
"Hospital": 50
}
}
}
}
}
}
}
8、扩展和附加功能
3D Tiles 定义了扩展以允许基本规范对新功能具有可扩展性。
8.1 扩展
扩展允许使用新功能扩展基本规范。可选extensions
字典属性可以添加到任何 3D Tiles JSON 对象,其中包含扩展名和扩展特定对象。以下示例显示了一个带有假设的供应商扩展的切片对象,该扩展指定了一个单独的碰撞体积。
{
"transform": [
4.843178171884396, 1.2424271388626869, 0, 0,
-0.7993325488216595, 3.1159251367235608, 3.8278032889280675, 0,
0.9511533376784163, -3.7077466670407433, 3.2168186118075526, 0,
1215001.7612985559, -4736269.697480114, 4081650.708604793, 1
],
"boundingVolume": {
"box": [
0, 0, 6.701,
3.738, 0, 0,
0, 3.72, 0,
0, 0, 13.402
]
},
"geometricError": 32,
"content": {
"uri": "building.glb"
},
"extensions": {
"VENDOR_collision_volume": {
"box": [
0, 0, 6.8,
3.8, 0, 0,
0, 3.8, 0,
0, 0, 13.5
]
}
}
}
Tileset或任何后代外部tileset中使用的所有扩展都应在顶级extensionsUsed
数组属性的条目tileset JSON 中列出,例如,
{
"extensionsUsed": [
"VENDOR_collision_volume"
]
}
加载和渲染tileset或任何后代外部tileset所需的所有扩展也应在顶级extensionsRequired
数组属性的条目tile extensionsRequired
集JSON 中列出,例如extensionsUsed
. 中的所有值extensionsRequired
也应存在于 中extensionsUsed
。
8.2 附加功能
该extras
属性允许将应用程序特定的元数据添加到任何 3D Tiles JSON 对象。以下示例显示了一个具有附加应用程序特定名称属性的 tile 对象。
{
"transform": [
4.843178171884396, 1.2424271388626869, 0, 0,
-0.7993325488216595, 3.1159251367235608, 3.8278032889280675, 0,
0.9511533376784163, -3.7077466670407433, 3.2168186118075526, 0,
1215001.7612985559, -4736269.697480114, 4081650.708604793, 1
],
"boundingVolume": {
"box": [
0, 0, 6.701,
3.738, 0, 0,
0, 3.72, 0,
0, 0, 13.402
]
},
"geometricError": 32,
"content": {
"uri": "building.glb"
},
"extras": {
"name": "Empire State Building"
}
}
完整的 JSON 模式可以在 tileset.schema.json
.
9、平铺格式规范
每个 tile 的content.uri
属性都是一个文件的 uri,该文件包含用于呈现 tile 的 3D 内容的信息。内容是下列格式之一的实例。
glTF 2.0是 3D Tiles的主要tile格式。glTF 是一个开放式规范,专为高效传输和加载 3D 内容而设计。glTF 资产包括单个tile的几何和纹理信息,并且可以扩展为包括元数据、模型实例化和压缩。glTF 可用于各种 3D 内容,包括:
- 异构 3D 模型。例如带纹理的地形和表面、3D 建筑外部和内部、大型模型
- 3D 模型实例。例如树木、风车、螺栓
- 海量点云
有关详细信息,请参阅glTF tile格式。
Tiles 也可能引用下面列出的旧版 3D Tiles 1.0 格式。这些格式在 3D Tiles 1.1 中已弃用,可能会在未来版本的 3D Tiles 中删除。
传统格式 | 用途 |
---|---|
异构 3D 模型 | |
3D 模型实例 | |
海量点数 | |
将不同格式的tile连接为一个 |
10、声明式样式规范
3D Tiles 包括用 JSON 定义的简明声明式样式,以及用一小部分 JavaScript 编写的表达式,增强了样式。
样式定义了特征的显示方式,例如(RGBshow
和color
半透明),使用基于特征属性的表达式。
以下示例将高度高于 90 的特征颜色为红色,其他颜色为白色。{ "color" : "(${Height} > 90) ? color('red') : color('white')"}
有关完整的详细信息,请参阅声明式样式规范。
原文链接:3D Tiles 1.1 Specification
BimAnt翻译整理,转载请标明出处