3D 瓦片格式规范

739 阅读23分钟

源地址:github.com/CesiumGS/3d…

简介

3D Tiles 专为流式传输和渲染大量 3D 地理空间内容而设计,例如摄影测量、3D 建筑、BIM/CAD、实例化特征和点云。它定义了一个分层数据结构和一组提供可渲染内容的图块格式。3D Tiles 没有为内容的可视化定义明确的规则;客户端可以随心所欲地可视化 3D Tiles 数据。

在 3D Tiles 中,tileset 是一组以空间数据结构(树)组织的瓦片。一个 tileset 由至少一个包含 tileset 元数据和 tile 对象树的 tileset JSON 文件描述,每个对象都可以引用以下格式之一的可渲染内容:

Batched 3D Model (b3dm):异构 3D 模型。例如。带纹理的地形和表面、3D 建筑外部和内部、大型模型。

Instanced 3D Model (i3dm):3D 模型实例。例如树木、风车、螺栓。

Point Cloud(pnts):大量的点

Composite(cmpt):将不同格式的瓦片连接成一个。

瓦片的内容(瓦片格式的单个实例)是一个二进制 blob,具有特定于格式的组件,包括特征表和批处理表。

瓦片内容表示一组特征,例如表示建筑物或树木的 3D 模型,或点云中的点。每个特征的位置和外观属性都存储在图块的特征表中,其他特定于应用程序的属性存储在批处理表中。客户端能够在运行时选择特征并检索它们的属性以进行可视化或分析。

b3dm模型和i3dm 模型格式建立在 glTF 之上,glTF 是一种开放规范,旨在高效传输 3D 内容。这些格式的瓦片内容在二进制主体中嵌入了一个包含模型几何和纹理信息的 glTF资源。点云格式不嵌入 glTF。

瓦片以树的形式组织,该树结合了层次细节级别 (HLOD) 的概念,以实现空间数据的最佳渲染。每个瓦片都有一个包围体,一个定义完全包围其内容的空间范围的对象。树具有空间连贯性;子瓦片的内容完全在父瓦片的包围体内。

1.png

tileset 可以使用2D 空间切片方案,类似于栅格和矢量切片方案(如 Web 地图切片服务 (WMTS) 或 XYZ 方案),这些切片方案在多个细节级别(或缩放级别)提供预定义切片。然而 tileset 的内容通常是不均匀的,或者说可能不容易仅在二维中组织,树可以是任何具有空间一致性的空间数据结构,包括 k-d 树、四叉树、八叉树和网格。

可以选择将 3D Tiles Style 或 style样式 应用于 tileset。该样式定义了每个特征的显示方式的表达式。

文件扩展名和媒体类型

3D Tiles 使用以下文件扩展名和媒体类型。

  • Tileset 文件使用 .json 扩展名和 application/json 媒体类型。
  • 瓦片内容文件使用特定于其瓦片格式规范的文件类型和媒体类型。
  • Tileset 样式文件使用 .json 扩展名和 application/json 媒体类型。

显式文件扩展名是可选的。有效的实现可能会忽略文件扩展名并通过标头中的magic字段来识别内容的格式。

JSON编码

3D Tiles 对 JSON 格式和编码有以下限制。

  1. JSON 必须使用不带 BOM 的 UTF-8 编码。
  2. 规范中定义的所有字符串(属性名称、枚举)仅使用 ASCII 字符集,并且必须以纯文本形式编写。
  3. JSON 对象中的名称(键)必须是唯一的,即不允许重复的键。

URLs

3D Tiles 使用 URIs 来引用瓦片内容。这些 URIs 可能指向相对外部引用 (RFC3986) 或者是在 JSON 中嵌入资源的数据 URI。嵌入式资源使用“数据”URL 方案 (RFC2397)。

当 URI 是相对路径的时,它的基准总是相对于引用的 tileset 的JSON 文件。

客户端实现需要支持相关的外部引用和嵌入式资源。此外,客户端实现可能支持其他方案(例如 http://)。所有 URI 都必须有效且可解析。

单位

所有直线距离的单位都是米。

所有角度都以弧度为单位。

概念

坐标参考系统 (CRS)

3D Tiles 使用右手笛卡尔坐标系。也就是说,x 和 y 的叉积产生 z。 3D Tiles 将 z 轴定义为局部笛卡尔坐标系。tileset 的全球坐标系通常位于 WGS 84 地球中心、地球固定 (ECEF) 参考框架 (EPSG 4978) 中,但它不一定一直如此,例如,发电厂可以在其局部坐标系中完全定义,以便在没有地理空间上下文的情况下与建模工具一起使用。

可以应用额外的瓦片变换以将瓦片的局部坐标系变换到父瓦片的坐标系。

区域边界体积使用地理坐标系(纬度、经度、高度)指定边界,特别是 EPSG 4979。

瓦片

瓦片包含用于确定是否呈现瓦片的元数据、对可呈现内容的引用以及任何子瓦片的数组

几何误差

瓦片被构造成包含层次细节级别 (HLOD) 的树,这样在运行时,客户端需要确定一个瓦片是否足够详细以进行渲染,以及瓦片的内容是否应该由更高分辨率的子图块连续细化。执行时将考虑最大允许屏幕空间误差 (SSE),该误差以像素为单位。

瓦片的几何误差定义了该瓦片的选择指标。它的值是一个非负数,用于指定瓦片对其源几何体的简化表示的误差(以米为单位)。作为源几何体的最简化版本的根瓦片将具有最大的几何误差。然后每个连续级别的子级将具有比其父级更低的几何误差,叶子瓦片具有接近 0 的几何误差。

在客户端实现中,几何误差与其他屏幕空间指标一起使用——例如,从图块到相机的距离、屏幕尺寸和分辨率来计算引入的 SSE,如果此瓦片被渲染而其子项未被渲染且引入的 SSE 超过允许的最大值,则细化瓦片并考虑渲染其子项。

几何误差是根据点密度、以米为单位的瓦片大小或该瓦片集特定的其他因素等指标制定的。一般来说,更高的几何误差意味着瓦片将被更积极地细化,并且子瓦片将更快地加载和渲染

细化

细化决定了较低分辨率的父级瓦片在选择较高的分辨率子级进行渲染时呈现的过程。允许的细化类型是替换(“REPLACE”)和添加(“ADD”)。如果一个瓦片具有替换细化,则子瓦片会代替父瓦片进行渲染,即不再渲染父瓦片。如果瓦片具有添加细化,则除了父瓦片之外还会渲染子瓦片。

一个 tileset 可以只使用替换细化,或只使用添加细化,或者添加和替换细化组合。

瓦片集的根瓦片必须提供细化类型;对于所有其他瓦片则是可选的。省略时,瓦片会继承其父项的细化类型

替换细化

如果一个瓦片使用替换细化,那么在细化时它会渲染它的孩子来代替它自己。

replacement_1.jpg

父级瓦片

replacement_2.jpg 细化后

添加细化

如果一个瓦片使用加法细化,那么在细化时它会同时渲染它自己和它的孩子。

additive_1.jpg

父级瓦片

additive_2.jpg

细化后

边界体积

边界体积定义了包含瓦片或瓦片内容的空间范围。为了支持各种数据集的紧密拟合体积,例如规则划分的地形、未与纬度或经度线对齐的城市或任意点云,边界体积的类型包括定向边界盒、边界球以及由最小和最大纬度、经度和高度定义的地理区域。

BoundingBox.jpg

边界盒子

BoundingSphere.jpg

边界球

BoundingRegion.jpg

边界区域

区域

boundingVolume.region 属性是一个包含六个数字的数组,用于定义具有纬度、经度和高度坐标的边界地理区域,顺序为[west, south, east, north, minimum height, maximum height],纬度和经度在 EPSG 4979 中定义的 WGS 84 基准中,以弧度为单位。高度以高于(或低于)WGS 84 椭球体的米为单位。

BoundingRegion (1).jpg

"boundingVolume": {
  "region": [
    -1.3197004795898053,
    0.6988582109,
    -1.3196595204101946,
    0.6988897891,
    0,
    20
  ]
}
盒子

boundingVolume.box 属性是一个包含 12 个数字的数组,用于在右手 3 轴 (x, y, z) 笛卡尔坐标系中定义定向边界框,其中 z 轴向上。前三个元素定义框中心的 x、y 和 z 值。接下来的三个元素(索引为 3、4 和 5)定义了 x 轴方向和半长。接下来的三个元素(索引 6、7 和 8)定义了 y 轴方向和半长。最后三个元素(索引 9、10 和 11)定义了 z 轴方向和半长。

BoundingBox (1).jpg

"boundingVolume": {
  "box": [
    0,   0,   10,
    100, 0,   0,
    0,   100, 0,
    0,   0,   10
  ]
}

boundingVolume.sphere 属性是一个包含四个数字的数组,用于定义边界球体。前三个元素定义右手 3 轴 (x, y, z) 笛卡尔坐标系中球体中心的 x、y 和 z 值,其中 z 轴向上。最后一个元素(索引为 3)定义了以米为单位的半径。

BoundingSphere (1).jpg

"boundingVolume": {
  "sphere": [
    0,
    0,
    10,
    141.4214
  ]
}

查看器请求体积

瓦片的 viewerRequestVolume 可用于组合异构数据集,并可与外部 tilesets 组合。

以下示例在 b3dm 瓦片中有一个建筑物,在 pnts 瓦片中建筑物内有一个点云。点云瓦片的 boundingVolume 是一个半径为 1.25 的球体。对于 viewerRequestVolume,它还有一个半径为 15 的更大球体。由于 geometricError 为零,因此当查看器位于由 viewerRequestVolume 定义的大球体内部时,点云图块的内容始终呈现(并最初请求)。

{
  "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.b3dm"
    }
  }, {
    "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.pnts"
    }
  }]
}

变换

瓦片变换

为了支持局部坐标系——例如,城市瓦片集中的建筑瓦片集可以在它自己的坐标系中定义,而建筑物内部的点云瓦片集也可以在它自己的坐标系中定义——每个瓦片都有一个可选的 transform属性。

transform 属性是一个 4x4 仿射变换矩阵,以列优先顺序存储,从瓦片的局部坐标系转换到父瓦片的坐标系——或者在根瓦片的情况下是瓦片集的坐标系。

transform属性适用于

  • tile.content

    • 每个特征的位置
    • 每个特征的法线都应由transform逆转置的左上角的 3x3 矩阵进行变换,以在使用比例时考虑正确的矢量变换。
    • content.boundingVolume在 EPSG:4979 坐标中明确表示,定义了content.boundingVolume.region时不需要。
  • tile.boundingVolume在 EPSG:4979 坐标中明确表示,定义了 tile.boundingVolume.region时不需要。

  • tile.viewerRequestVolume在 EPSG:4979 坐标中明确表示,定义tile.viewerRequestVolume.region时不需要。

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变换

批处理 3D 模型(b3dm)和实例化 3D 模型(i3dm)瓦片嵌入了 glTF,它定义了自己的节点层次结构并使用 y-up 坐标系。任何特定于瓦片格式的转换和 tile.transform 属性都会在解析这些转换后应用。

glTF 节点层次结构

首先,根据 glTF 规范应用 glTF 节点层次结构转换。

y-up to z-up

接下来,为了与 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]

更笼统地说,转换的顺序是:

  1. glTF 节点层次结构转换

  2. glTF y-up 到 z-up 变换

  3. 任何瓦片格式的特定转换

    b3dm 特征表 可以定义 RTC_CENTER,用于平移模型顶点。

    i3dm 特征表定义每个实例的位置、法线和比例。这些用于创建应用于每个实例的其 4x4 仿射变换矩阵。

  4. 瓦片转换

实施说明:当处理本质上是 z-up 的源数据时,例如 WGS 84 坐标中的数据或本地 z-up 坐标系中的数据,常见的工作流程是:

  • 网格数据(包括位置和法线)未被修改 - 它们保持 z-up。
  • 根节点矩阵指定列主要 z-up 到 y-up 变换。这会将源数据转换为 glTF 所需的 y-up 坐标系。
  • 在运行时,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"
 }
]
示例

对于图块集的计算变换(上面代码中的 transformToRoot)的示例,请考虑:

tileTransform.png

每个图块的计算变换是:

  • TO: [T0]
  • T1: [T0][T1]
  • T2: [T0][T2]
  • T3: [T0][T1][T3]
  • T4: [T0][T1][T4]

tile 内容中的位置和法线也可能在 tile 变换之前应用特定于 tile 的变换(在表示仿射变换的相乘之前)。下面是一些例子:

  • b3dm 和 i3dm tiles 嵌入了 glTF,它定义了自己的节点层次结构和坐标系。 tile.transform 在这些转换解决后应用。
  • i3dm 的特征表定义了每个实例的位置、法线和比例。这些用于创建每个实例的 4x4 仿射变换矩阵,这些矩阵在 tile.transform 之前应用于每个实例。
  • 压缩属性,例如 i3dm 和 pnts 的特征表中的 POSITION_QUANTIZED,以及 pnts 中的 NORMAL_OCT16P 应该在任何其他转换之前解压缩。

因此,上述示例的完整计算转换为:

  • TO: [T0]
  • T1: [T0][T1]
  • T2: [T0][T2][pnts-specific的转换,包括 RTC_CENTER(如果已定义)]
  • T3: [T0][T1][T3][b3dm-specific 变换,包括 RTC_CENTER(如果已定义)、坐标系变换和 glTF 节点层次结构]
  • T4: [T0][T1][T4][i3dm-specific 转换,包括每个实例变换、坐标系变换和 glTF 节点层次结构]
实施案例

本节是不规范的

下面的 JavaScript 代码展示了如何使用 Cesium 的 Matrix4 和 Matrix3 类型来进行计算。

function computeTransforms(tileset) {
    var t = tileset.root;
    var transformToRoot = defined(t.transform) ? Matrix4.fromArray(t.transform) : Matrix4.IDENTITY;
​
    computeTransform(t, transformToRoot);
}
​
function computeTransform(tile, transformToRoot) {
    // Apply 4x4 transformToRoot to this tile's positions and bounding volumes
​
    var inverseTransform = Matrix4.inverse(transformToRoot, new Matrix4());
    var normalTransform = Matrix4.getRotation(inverseTransform, new Matrix3());
    normalTransform = Matrix3.transpose(normalTransform, normalTransform);
    // Apply 3x3 normalTransform to this tile's normals
​
    var children = tile.children;
    var length = children.length;
    for (var i = 0; i < length; ++i) {
        var child = children[i];
        var childToRoot = defined(child.transform) ? Matrix4.fromArray(child.transform) : Matrix4.clone(Matrix4.IDENTITY);
        childToRoot = Matrix4.multiplyTransformation(transformToRoot, childToRoot, childToRoot);
        computeTransform(child, childToRoot);
    }
}

瓦片JSON

瓦片JSON 对象包含以下属性

tile.png

以下示例显示了一个非叶子瓦片:

{
  "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.b3dm"
  },
  "children": [...]
}

boundingVolume 定义包围瓦片的体积,并用于确定在运行时渲染哪些图块。上面的示例使用了区域体积,但也可以使用其他边界体积,例如盒子或球体。

geometricError 属性是一个非负数,用于定义错误,以米为单位,如果渲染此图块而其子项未呈现则引入。在运行时,几何误差用于计算屏幕空间误差 (SSE),该误差以像素为单位。SSE 确定图块对于当前视图是否足够详细,或者是否应考虑其子项。

可选的 viewerRequestVolume 属性(上面未显示)定义了一个体积,使用与 boundingVolume 相同的架构,在请求图块内容之前以及根据几何错误优化图块之前,查看器必须在其中。

refine 属性是一个字符串,对于替换细化是“REPLACE”,对于增加细化是“ADD”。瓦片集的根瓦片是必须的;所有其他图块都是可选的。一个 tileset 可以使用添加和替换细化的任意组合。当省略 refine 属性时,它是从父瓦片继承的。

content 属性是一个对象,其中包含有关 tile 的可渲染内容的元数据。 content.uri 是指向瓦片二进制内容的 uri,或另一个 tileset JSON 。

content.uri 不需要文件扩展名。内容的 tile 格式可以通过其标头中的magic字段来标识,或者如果内容是 JSON,则作为外部 tileset。

content.boundingVolume 属性定义了一个类似于顶级 boundingVolume 属性的可选边界体积。但与顶级 boundingVolume 属性不同的是,content.boundingVolume 是一个紧密配合的边界体积,仅包含瓦片的内容。boundingVolume 提供空间连贯性,而 content.boundingVolume 支持紧密的视锥体剔除,避免渲染不在潜在视图体积内的任何内容。当它未定义时,瓦片的边界体积仍用于剔除。

下面的屏幕截图显示了金丝雀码头根瓦片的边界体积。boundingVolume,以红色显示,包围了 tileset 的整个区域; content.boundingVolume 以蓝色显示,仅包含根图块中的四个特征(模型)。

contentsBox.png

可选的 transform 属性(上面未显示)定义了一个 4x4 仿射变换矩阵,它按照 Tile 变换部分中的描述变换 tile 的内容、boundingVolume 和 viewerRequestVolume。

children 属性是一组定义子瓦片的对象。每个子瓦片的内容都完全包含在其父瓦片的 boundingVolume 中,通常,geometricError小于其父瓦片的geometricError。对于叶子瓦片,此数组的长度为零,并且可能未定义子级

Tileset JSON

3D Tiles 使用一个主要的 tileset JSON 文件作为入口点来定义一个 tileset。入口和外部 tileset JSON 文件都不需要遵循特定的命名约定。

这是用于金丝雀码头的 tileset JSON 的子集

{
  "asset" : {
    "version": "1.0",
    "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.b3dm",
      "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 建筑的,所以每个 tile 都有建筑模型,每个建筑模型都有一个 Height 属性。properties 中每个对象的名称与每个特征属性的名称相匹配,它的值定义了它的最小和最大数值,这很有用,例如为样式创建颜色渐变。

geometricError 是一个非负数,它定义了错误,以米为单位,确定是否渲染了 tileset。在运行时,几何误差用于计算屏幕空间误差 (SSE),该误差以像素为单位。 如果 SSE 未超过所需的最小值,则不应渲染瓦片集,并且不应考虑渲染其瓦片。

root 是一个对象,它使用上一节中描述的瓦片 JSON 定义根瓦片。 root.geometricError 与 tileset 的顶级 geometricError 不同。tileset 的 geometricError 在运行时用于确定 tileset 的根 tile 渲染的 SSE;root.geometricError 在运行时用于确定渲染根瓦片子项的 SSE。

外部瓦片集

要创建树中树,tile 的 content.uri 可以指向外部 tileset(另一个 tileset JSON 文件的 uri)。例如,这可以将每个城市存储在一个 tileset 中,然后拥有一个全局 tilesets 。

tilesets.png

当瓦片指向外部瓦片集时,瓦片:

  • 不能有任何孩子; tile.children 必须是未定义的或空数组。
  • 不能用于创建循环,例如,通过指向包含该瓦片的同一个瓦片集文件或通过指向另一个瓦片集文件然后指向包含该瓦片的初始文件。
  • 将被 tile 的transform和 root tile 的transform同时变换。例如,在以下引用外部瓦片集的瓦片集中,T3 的计算变换为T0T2

tileTransformExternalTileset.png

如果外部 tileset 定义了 asset.tilesetVersion,这会覆盖父瓦片集中的值。如果外部 tileset 没有定义 asset.tilesetVersion,则该值继承自父 tileset(如果已定义)。

边界体积空间相干性

如上所述,树具有空间连贯性;每个瓦片都有一个完全包围其内容的边界体积,子瓦片的内容完全在父瓦片的边界体积内。这并不意味着孩子的边界体积完全在其父边界体积内。例如

parentBoundingSphere.jpg

地形瓦片的边界球体

childBoundingSphere.jpg

四个子图块的边界球体。子级的内容完全在父级的包围体内,但子级的包围体不在,因为它们没有紧密贴合。

空间数据结构

3D Tiles 结合了分层细节层次 (HLOD) 的概念,以优化空间数据的渲染。一个 tileset 由一棵树组成,由根定义,递归地,它的子瓦片可以由不同类型的空间数据结构组织。

运行时引擎是通用的,将渲染由 tileset 定义的任何树。可以使用切片格式和细化方法的任意组合,从而能够灵活地支持异构数据集。

tileset 可以使用类似于栅格和矢量切片方案(如 Web 地图切片服务 (WMTS) 或 XYZ 方案)的 2D 空间切片方案,这些切片方案在多个细节级别(或缩放级别)提供预定义切片。然而,由于 tileset 的内容通常是不均匀的,或者可能不容易仅在二维中组织,因此其他空间数据结构可能更佳。

下面简要描述了 3D Tiles 如何表示各种空间数据结构。

四叉树

当每个瓦片具有四个均匀细分的子项(例如,使用中心纬度和经度)时,将创建四叉树,类似于典型的 2D 地理空间切片方案。可以省略空的子瓦片。

quadtree.png

经典的四叉树细分

3D Tiles 启用四叉树变体,例如不均匀细分和紧边界体积(与边界相反,例如,父图块的全部 25%,这对于稀疏数据集来说是一种浪费)。

quadtree-tight.png

每个孩子周围都有紧密的边界体积的四叉树

例如,这是 Canary Wharf 的根瓦片及其子瓦片,请注意左下角,边界体积不包括左侧没有建筑物出现的水域:

nonUniformQuadtree.png

3D Tiles 还支持其他四叉树变体,例如松散四叉树,其中子瓦片重叠但空间连贯性仍然保留,即父瓦片完全包围其所有子瓦片。此方法可用于避免跨图块拆分要素(例如 3D 模型)。

quadtree-overlap.png

具有不均匀和重叠瓦片的四叉树。

下面,绿色建筑物在左孩子中,紫色建筑物在右孩子中。请注意,瓷砖重叠,因此中间的两座绿色和一座紫色建筑没有分开。

looseQuadtree.png

K-d树

当每个瓦片有两个子瓦片时,它们被平行于 x、y 或 z 轴(或纬度、经度、高度)的分割平面分开,就会创建一个 k-d 树。分裂轴通常随着树的级别增加而循环旋转,并且可以使用中值分裂、表面积启发式或其他方法来选择分裂平面。

kdtree.png

示例 k-d 树。注意非均匀细分。

请注意,k-d 树不像典型的 2D 地理空间切片方案那样具有均匀的细分,因此可以为稀疏和非均匀分布的数据集创建更平衡的树。

3D Tiles 支持 k-d 树的变体,例如多路 k-d 树,其中在树的每个叶子处,沿轴有多个拆分。不是每个瓦片有两个孩子,而是有 n 个孩子。

八叉树

八叉树通过使用三个正交的分裂平面将一个瓦片细分为八个孩子来扩展四叉树。与四叉树一样,3D Tiles 允许对八叉树进行变体,例如非均匀细分、紧边界体积和重叠子项。

octree.png

传统的八叉树细分。

pointcloud-octree.png

使用加法细化对点云进行非均匀八叉树细分。

网格

3D Tiles 通过支持任意数量的子瓦片来实现统一、非统一和重叠的网格。例如,这是剑桥非均匀重叠网格的自上而下视图:

grid.png

3D tiles利用空瓦片:那些具有边界体积但没有内容的瓦片。 由于不需要定义图块的内容属性,因此可以使用空的非叶子瓦片来加速具有分层剔除的非均匀网格。 这实质上创建了一个没有层次细节层次 (HLOD) 的四叉树或八叉树。

指定扩展和特定于应用程序的附加功能

3D Tiles 定义了扩展以允许基本规范具有新功能的可扩展性,以及允许特定于应用程序的元数据的附加功能。

扩展

扩展允许使用新功能扩展基本规范。可选的扩展字典属性可以添加到任何 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.b3dm"
  },
  "extensions": {
    "VENDOR_collision_volume": {
      "box": [
        0,     0,    6.8,
        3.8,   0,    0,
        0,     3.8,  0,
        0,     0,    13.5
      ]
    }
  }
}

tileset 或任何后代外部 tilesets 中使用的所有扩展必须列在顶级 extensionsUsed 数组属性的条目 tileset JSON 中,例如,

{
    "extensionsUsed": [
        "VENDOR_collision_volume"
    ]
}

加载和渲染 tileset 或任何后代外部 tilesets 所需的所有扩展也必须在顶级 extensionsRequired 数组属性中的条目 tileset JSON 中列出,这样 extensionsRequired 是 extensionsUsed 的子集。extensionsRequired 中的所有值也必须存在于 extensionsUsed 中。

附加功能

extras 属性允许将特定于应用程序的元数据添加到任何 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.b3dm"
  },
  "extras": {
    "name": "Empire State Building"
  }
}