3D-Tiles Github Readme 翻译(主要是tileset.json的格式)

流化海量异质三维地理空间数据集的规范

 开发团队:Cesium team ;基于 glTF

编辑:Patrick Cozzi, @pjcozzipcozzi@agi.com. 翻译:ChrisWang, @ecnuzlwangwzlh100@126.com

可用实例:

目录

资源

  • 3D Tiles介绍 - 3D Tiles的制作动机和基本原理。如果你是新手,请先读这篇文章。

  • 新闻

    • Cesium论坛上的3D Tiles帖子 – 获取3D Tiles的最新消息,可以在帖子下面提问
  • 在Cesium中的实践

  • 简单的数据

  • 演讲

    • 3D Tiles 的动机和生态的更新 (pdf) ,在2016年三月的OGC Technical Committee Meeting。

    • 3D Tiles 介绍 (pdf) ,在2015年的SIGGRAPH,Cesium BOF

规范现状

目前3D Tiles 规范版本是1.0预览版(pre-1.0,在tileset.json文件中用"version": "0.0"声明)。我们希望1.0正式版的草案和在Cesium中的应用能在2016年秋天和大家见面。

对于规范中正在进行的工作查看这个repo 和 issues.

简介

在3D Tiles中,一个tileset 就是一系列空间数据结构(树)组织下的瓦片(tiles)。每一个瓦片都有一个完全包住它所有内容的边界体(bounding
volume),树状数据结构具有空间连续性。父对象的边界体包含子瓦片的所有内容。为了灵活应用,数据结构可以是具有空间连续性的不同的树,包括k-d树,四叉树,八叉树和网格。

为了支持紧凑吻合不同数据集中的地理实体(规则分割的地形,不按照经纬网分布的城市群,随机的点云),边界体可以是一个固定的盒状边界,也可以是一个球状边界,还可以是一个由最大最小经纬度和高度定义的地理区域。

一个瓦片索引一个或者一系列要素,比如表达建筑物或树木的三维模型,点云中的点,矢量数据集中点线面要素。这些要素需要捆绑在一起,形成单独的一个要素,来减少客户端加载时间,防止WebGL的绘制请求过载。

瓦片元数据

瓦片的元数据(并非真实的内容)定义在JSON文件中。如下:

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

boundingVolume.region 属性是包含六个元素的数组对象,用于定义边界地理区域,格式是[west,
south, east, north, minimum height, maximum height]。

经度和维度以弧度为单位,高度以米为单位(高于或低于WGS84椭球体)除了 region,也有其他边界体可以用,比如 box 和 sphere。

geometricError 属性是一个非负数值,用于定义以米为单位的误差,在这个瓦片渲染而它的子瓦片没有渲染的时候引入。在应用运行时,几何误差用于计算屏幕空间误差(Screen-Space
Error
,SSE),比方说以像素为单位的误差。SSE决定层次细节程度的细化。例如,决定一个瓦片在当前视图中是否足够精细,或者是否需要考虑加载它的子瓦片。

refine属性是一个字符串,不是 replace(用于替换细化)就是add(用于叠加细化)
在一个tileset文件的根瓦片中就需要填写这个属性。当refine省略时,它就继承了父对象的细化方法。

content属性是一个包含瓦片内容和链接的元数据对象。 content.url 是一个绝对或者相对路径,指向瓦片内容。在上面的示例代码中,路径2/0/0.b3dm具有TMS切片方案{z}/{y}/{x}.extension但这不是必须的。查看roadmap Q&A.

content.url 中的文件后缀名定义了瓦片格式(tile format)。url可以是另一个tileset.json文件,这样就创建了一个tileset中的tileset。参考External tilesets

content.boundingVolume定义了另一个边界体,与最高层级的boundingVolume属性相似。但是不同于boundingVolume属性,content.boundingVolume只是一个恰好紧紧包围瓦片内容的边界体。这样的设定用于替换细化;boundingVolume提供空间连续性,content.boundingVolume用于紧凑视锥体的选择(tight view frustum culling)下面的截图展示的是代表 Canary Wharf根瓦片的边界体。红色的边框就是boundingVolume,包围了整个tileset的区域;蓝色边框就是content.boundingVolume,包含了根瓦片中的四个要素(模型)。

Content是可以选填的,如果没有指定,瓦片的边界体还是一样用于视锥体选择(参见 Grids)。

Children是包含定义了子瓦片对象的数组,参见下一节。

Coordinate System and Units

3D Tiles use a right-handed Cartesian coordinate system, that is, the cross
product of x and y yields z. 3D Tiles define the z axis as up for local
Cartesian coordinate systems (see the Tile transform section).
A tileset's global coordinate system will often be WGS84 coordinates,
but it doesn't have to be, e.g., a power plant may be defined fully in its local
coordinate system for using with a modeling tool without a geospatial context.

The units for all linear distances are meters.

All angles are in radians.

3D Tiles do not explicitly store Cartographic coordinates (longitude, latitude,
and height); these values are implicit in WGS84 coordinates, which are efficient
for the GPU to render since they do not require a non-affine coordinate
transformation. A 3D Tiles tileset can include application-specific metadata,
such as Cartographic coordinates, but the semantics are not part of the 3D Tiles
specification.

Tile transform

To support local coordinate systems, e.g., so a building tileset inside a city
tileset can be defined in its own coordinate system, and a point cloud tileset
inside the building could, again, be defined in its own coordinate system, each
tile has an optionaltransform property.

The transform property is a 4x4 affine transformation matrix, stored in
column-major order, that transforms from the tile's local coordinate system to
the parent tile's coordinate system, or tileset's coordinate system in the case
of the root tile.

The transform property applies to:

  • tile.content

    • Each feature's position.

    • Each feature's normal should be transformed by the top-left 3x3 matrix of the inverse-transpose of transform to account for correct vector transforms when scale is used.

    • content.boundingVolume, except when content.boundingVolume.region is defined, which is explicitly in WGS84 coordinates.

  • tile.boundingVolume, except when tile.boundingVolume.region is defined, which is explicitly in WGS84 coordinates.

  • tile.viewerRequestVolume, except when tile.viewerRequestVolume.region is defined, which is explicitly in WGS84 coordinates.

The transform property does not apply to geometricError, i.e., the scale defined
by transform does not scale the geometric error; the geometric error is always
defined in meters.

When transform is not defined, it defaults to the identity matrix:

[
    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
]

The transformation from each tile's local coordinate to the tileset's global
coordinate system is computed by a top-down traversal of the tileset and
post-multiplying a child's transform with its parent's transform like a
traditional scene graph or node hierarchy in computer graphics.

The following JavaScript code shows how to compute this using
Cesium's Matrix4 and Matrix3 types.

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 k = 0; k \< length; ++k) {
        var child = children[k];
        var childToRoot = defined(child.transform) ? Matrix4.fromArray(child.transform)
: Matrix4.clone(Matrix4.IDENTITY);
        childToRoot = Matrix4.multiplyTransformation(transformToRoot, childToRoot, childToRoot);
        computeTransform(child, childToRoot);
    }
}

` For an example of the computed transforms (transformToRoot in the code above)
for a tileset, consider:

The computed transform for each tile is:

  • TO: [T0]

  • T1: [T0][T1]

  • T2: [T0][T2]

  • T3: [T0][T1][T3]

  • T4: [T0][T1][T4]

The positions and normals in a tile's content may also have tile-specific
transformations applied to them before the tile'stransform (before indicates
post-multiplying for affine transformations). Some examples are:

  • b3dm and i3dm tiles embed glTF, which defines its own node hierarchy, where each node has a transform. These are applied before tile.transform.

  • i3dm's Feature Table defines per-instance position, normals, and scales. These are used to create per-instance 4x4 affine transform matrices that are applied to each instance before tile.transform.

  • Compressed attributes, such as POSITION_QUANTIZED in the Feature Tables for i3dm, pnts, and vctr, and NORMAL_OCT16Pin pnts should be decompressed before any other transforms.

Therefore, the full computed transforms for the above example are:

  • TO: [T0]

  • T1: [T0][T1]

  • T2: [T0][T2][pnts-specific Feature Table properties-derived transform]

  • T3: [T0][T1][T3][b3dm-specific transform, including the glTF node hierarchy]

  • T4: [T0][T1][T4][i3dm-specific transform, including per-instance Feature Table properties-derived transform and the glTF node hierarchy]

Viewer request volume

A tile's viewerRequestVolume can be used for combining heterogeneous datasets,
and can be combined with external tilesets.

The following example has a building in a b3dm tile and a point cloud inside the
building in a pnts tile. The point cloud tile'sboundingVolume is a sphere with a
radius of 1.25. It also has a larger sphere with a radius of 15 for
theviewerRequestVolume. Since the geometricError is zero, the point cloud tile's
content is always rendered (and initially requested) when the viewer is inside
the large sphere defined by 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": {
        "url": "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": {
        "url": "points.pnts"
    }
}]

TODO: screenshot showing the request vs. bounding volume

For more on request volumes, see the sample
tileset
 and demo
video
.

tileset.json

tileset.json定义了一个tileset。下面是用于构建Canary Wharf 的tileset的一个子集(参见完整的 tileset.json):

{
    "asset" : {
        "version": "0.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,
        "content": {
            "url": "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元数据属性的对象。其中version属性是定义3D Tiles版本的字符串。版本号定义了tileset.json的JSON格式和瓦片格式的基础设定。tilesetVersion属性是一个选填的字符串,用于定义特定应用中tileset的版本,可以用于更新tileset。

Properties是一个包含tileset中每个要素每个属性信息的对象。这个tileset.json代码片段是用于三维建筑,每一个瓦片都有建筑物模型,每个模型都有Height属性(参考Batched 3D Model瓦片格式中的Batch Table)。

properties 每个对象的名称都与每个要素中对应属性的名称相同,定义了他们的minimum 和 maximum数值,在自定义色阶样式时很有用。

geometricError是一个非负数值,在tileset没有渲染时定义米制误差。

root用于定义根瓦片(参考上一节)。root.geometricError与最高层级tileset.json的geometricError不同。tileset.json的geometricError定义的误差用在整个tileset还没有被渲染的时候;root.geometricError定义的误差用在只有根瓦片被渲染的时候。

root.children是定义子瓦片对象的数组。每个子瓦片都有boundingVolume,这个边界体被父瓦片的boundingVolume完全包围,通常子瓦片的 geometricError要小于父瓦片的geometricError。对于叶子瓦片而言,子瓦片数组的长度为0,
所以 children也可以不定义。

更详细的tileset.sjon的JSON书写格式参见schema

想了解tileset.json如何涵盖海量瓦片,参考下面的问答环节 。

外部tilesets

要想在树结构中加入子树,可以用瓦片中的content.url 属性指向一个外部tileset路径(另一个tileset.json)。这一功能可以将一个城市区域保存于一个tileset中,然后用一个全局的tileset保存所有城市的tileset。

当一个瓦片指向一个外部tileset时,这个瓦片:

  • 不能拥有子瓦片,tile.children必须设置为undefined 或者空数组

  • 与外部tileset的根瓦片在语义上相同,所以:

    • root.geometricError === tile.geometricError,

    • root.refine === tile.refine,

    • root.box === tile.content.box (或者当 tile.content.box 是 undefined时,root.box === tile.box ).

  • 不能循环使用,比如,它不能指向包含这个瓦片的tileset,也不能指向另一个tileset,这个tileset中包含指向盖瓦片所在tileset的瓦片。

边界体的空间连续性

如上文所述,树状结构具有空间连续性;每一个瓦片都有一个边界体紧紧包围其中的内容,子瓦片的内容完全包含在父对象的边界体中。但是这不表示子瓦片的边界体完全在父瓦片的边界体中。比如:

用于地形瓦片的球状边界体(Bounding sphere)

四个子瓦片的球状边界体。子瓦片的内容完全在父瓦片的边界体中,但是子瓦片的球状边界体却超出了父瓦片的球状边界体,两者不是完全重合。

创建空间数据结构

由root和它的children组成的树状结构可以使用不同类型的空间数据结构。此外,不同瓦片格式和细化方法(替换或叠加)也可以结合使用,这使得3D Tiles对异质数据集的支持更加灵活。

为数据集生成tilest.json的文件,选择最优化的树状结构的方法,要取决于转换工具。像Cesium这样的实时引擎拥有通用的方法,可以渲染tileset.json定义的任何树状结构。下面是3D Tiles可视化不同空间数据结构的方法简介。

K-d树

K-d树是的每个父瓦片有两个子瓦片,用平行于x轴、y轴或者z轴(经度,维度,高度)的切面分开。切面对应的轴是随着层级沿树枝向下增加,而依次循环旋转的。切面的选择可以用中位数切分,表面积启发式算法或者其他的方法。

k-d 树实例:注意切分是非均匀的

注意,k-d树不像是典型的二维地理空间切片方法那样均匀的切分,所以可以针对稀少和非均匀的数据分布制定出更均衡的树状数据结构。

3D
Tiles支持k-d树的变换,比如 多路k-d树 (每个节点沿着轴被分割成很多份,不止两个子节点,可以有n个子节点。

四叉树

四叉树将区域均匀分成四个子区域(比如沿着中心经线和纬线),与典型的而为地理空间切片方法相似。空的子瓦片可以忽略。

经典四叉树切分

3D Tiles支持使用四叉树的变换,比如非均匀切分和紧密边界体(tight bounding volumes)。一般的边界体占据父瓦片的25%,当内容稀少时,空间就被浪费了。

四叉树中子瓦片的紧密边界体

比如,下图是Canary Wharf的根瓦片和子瓦片。请看左下角,边界体不包含左侧的河流,那是因为河流上没有建筑物。

3D Tiles
还支持松散的四叉树,子瓦片可能有重叠,但是空间连续性依然保留,举个例子,一个父瓦片完全包含所有的子瓦片。这种方法避免了跨瓦片的要素(如三维模型)被切割。

四叉树中非均匀且有重叠的瓦片

下图中,绿色建筑物在左侧子瓦片中,紫色建筑物在右侧子瓦片中。可以看到子瓦片之间有重叠,这样在中间的绿色和紫色建筑物就不会被切割。

八叉树

八叉树是四叉树的延伸,用三个正交的切面将瓦片空间分割成八个子瓦片。和四叉树一样,3D Tiles支持八叉树的变换:非均匀切分,紧密边界体和重叠子瓦片。

传统的八叉树切分

使用非均匀八叉树切分和叠加细化的点云数据,法国沙普的the Church of St Marie by Prof.
Peter Allen, Columbia University Robotics Lab. Scanning by Alejandro Troccoli
and Matei Ciocarlie

网格

3D
Tiles支持均匀、非均匀和重叠的网格结构,子瓦片中的网格数目可以是自定义的。下图是从上往下看的Cambridge非均匀重叠网格:

3D
Tiles有效利用了空的瓦片(有边界体但是没有内容的瓦片)。因为这些瓦片的content 属性不需要定义,非叶子节点的空瓦片可以用于加速非均匀网格的层级剔除(hierarchical
culling)。这种方法实际上是创建了没有层级细节程度的四叉树或者八叉树。

瓦片格式

每个瓦片的content.url 属性指向一个瓦片格式(见上文现状”章节的列表)。

一个tileset可以包含上述格式的任意组合。3D Tiles可以使用组和瓦片(Composite tile),实现在相同的瓦片中支持不同格式。

样式定义

利用样式定义按高度为建筑物设色

3D Tiles支持使用简单的JSON和一小部分增广的JavaScript表达式实现样式定义。

样式通常利用基于要素属性的表达式定义一个要素的显隐和颜色(RGB和透明度),比如:

{
    "color" : "(\${Temperature} \> 90) ? color('red') : color('white')"
}

这段代码将温度高于90的要素设置为红色,其他的为白色。

更多细节请看Declarative Styling 规范。

技术路线和问答

常见为题

现在能使用3D Tiles吗?

我们期待3D Tiles的第一个版本在2016年秋天发布。如果可以接受规范的改变,你可以查看和使用Cesium中的 3d-tiles 分支。

3D Tiles只能在Cesium中使用吗?

不是的,3D Tiles是一个针对流化海量异质三维地理空间数据集的通用标准。Cesium团队提出了这个想法,因为我们需要一个开放的格式用于优化三维内容在Cesium中成流。创立Cesium的公司AGI是也在开发生成3D
Tiles的工具。我们期待更多可视化引擎和转换工具使用3D Tiles。

3D Tiles和glTF的关系是什么?

glTF是WebGL的运行资产格式(runtime asset format),是Khronos(同时也是WebGL和COLLADA规范的维护团队)开发的,用于三维模型的开放标准。Cesium使用glTF作为三维模型的标准格式,同时Cesium团队对glTF和开源COLLADA2GLTF转换工具的开发也有很多贡献。在Cesium中,我们推荐您用glTF构建单个实体,比如飞机、人物、或者一个三维建筑。

3D
Tiles的诞生是为流化海量地理空间数据集服务的,单个glTF模型在这方面是受限制的。鉴于glTF在渲染上的优化性能,Cesium成熟的glTF加载器和glTF转换工具的存在,3D
Tiles
将glTF格式用于一些瓦片格式,比如 b3dm (用于可视化三维建筑)。为了在二进制文件中嵌入glTF并且避免base64编码或多文件耗费,我们开发了二进制glTF拓展模块(KHR_binary_glTF)。

使用这种方法可以同时提升Cesium,glTF和3D Tiles。比如,把网格压缩成glTF有利于三维模型在Cesium,glTF生态系统和3D
Tiles的使用。

3D Tiles支持实时编辑吗?

三维建筑的常用场合是流化成一个城市数据集;根据属性(建筑物高度)为建筑物上色;隐藏一些建筑物,替换成高分辨率三维模型。在3D Tiles中,这类编辑是可以实时进行的。

在常用的情境中,对建筑物和矢量数据进行实时几何编辑,然后保存到3D Tile中也是可以实现的,但是这不是当前的重点。然后,定义样式更加简单,因为它可以在不改变3D
Tiles树状结构的前提下实时进行,这也是当前的重点工作。

3D Tiles会支持地形吗?

是的,像quantized-mesh这样的瓦片能够很好的被3D Tiles支持,所以Cesium可以使用相同的流化代码(之所以说“像quantized-mesh这样的”,是因为一些元数据,比如边界体和水平剔除,可能需要不一样的组织形式,或者转换成tileset.json的样式)。

然而,因为Cesium已经出色的完成了地形的成流工作,所以短期内这不会是我们的重点。

3D Tiles会支持影像吗?

是的,提供包含地形和影像的优化底图是有可能的(就像三维模型包含几何形状和材质一样)。但是如何对影像进行三维切片也是一个公开研究的问题。在二维中,视域内只有一个纬度的细节程度(z图层)。在三维中,尤其是水平视角下,拥有多个纬度的细节程度的瓦片是十分接近的。如何美化瓦片之间的缝隙?这需要工具和实时支持。

就像地形一样,因为Cesium已经很好的流化了影像,所以短期内这也不会是重点工作。

3D Tiles会取代KML吗?

在很多方面会取代。KML的regions和network links在流化网页端海量三维地理空间数据集的时候很吃力。3D
Tiles为网页端而生,而且优化了成流;使用了真正的HLOD;多边形不需要三角划分;还有一系列的优点。

技术问题

3D Tiles如何支持异质的数据集?

地理空间数据集市异质的:三维建筑物,地形,点云数据,矢量数据等都是不同的。

3D
Tiles通过tileset中不同的瓦片格式支持异质数据。比如,一个tileset可能包含三位建筑物,实例三维树木,点云数据和其他数据格式的瓦片。

3D
Tiles也可以通过组合瓦片(Composite tile)格式连接不同瓦片格式,在同一瓦片中支持异质数据集。在上面的例子中,一个瓦片可以有一个短的文件头,后面跟着三维建筑,实例三维树木和点云数据的内容。

跨瓦片(不同的瓦片格式在一个tileset中)和瓦片内(不同的瓦片格式在一个组合瓦片中)对异质数据集的支持使得转换工具可以在请求数量,特定类型分区优化和显隐图层成流之间进行平衡。

tileset.json就是3D Tiles规范的最终形式吗?

是的,知道tileset的元数据和未加载的瓦片都是有必要的,这样就只有可见的瓦片被请求。然而,当放大到上百万瓦片时,单个包含所有树状结构元数据的tileset.json文件会很大。

3D
Tiles已经支持树中的树。content.url可以指向另一个tileset.json,所以转换工具可以将单个tileset分割成任意数量的tileset.json文件,相互索引。

也有一下其他的方法解决:

  • 将子树的元数据放在瓦片中而不是tileset.json中。每一个瓦片都会有一个包含子瓦片或孙子瓦片边界体(或者其他数据)的文件头。

  • 像传统切片那样明确的瓦片布局(比如TMS的 z/y/x)。这样做的不足是定义了特定的空间分区,然而3D Tiles是可以通用的支持四叉树、八叉树和k-d树等多种数据结构的。用两三个明确切片解决普通案例,借此弥补通用空间数据结构可能是一种较为均衡的方法。

如何获取层级为n的瓦片?

概括的来说,当视角放大到距离地形很近,我们不希望载入所有父瓦片,而是想要跳过这些直接载入当前视图中的高分辨率瓦片,3D Tiles应该如何实现这个功能的呢?

这个3D Tiles话题需要更多的研究,不过回答和上面的问题类似:1. 如果树状结构的架构可以快速遍历,就能找到想要的瓦片;2. 明确的切片布局可以用于定义分区。

3D Tiles支持水平剔除吗?

因为水平剔除(horizon culling)对地形非常有用,3D
Tiles应该会支持这个功能的元数据。我们还没有考虑过这个问题,因为我们当前的首要任务是3D
Tiles对3D建筑物的支持,水平剔除并不实用。

屏幕空间误差是驱动细化的唯一指标吗?

在运行时,瓦片的geometricError用于计算驱动细化的屏幕空间误差。我们计划用虚拟多分辨率屏幕空间误差(Virtual Multiresolution Screen Space
Error
 ,VMSSE)扩充这个功能,将遮蔽(occlusion)考虑在内。这可以在运行时完成,不需要流化额外的瓦片元数据。雾化(fog)也可以用于解决远处SSE增加的问题。

然而,我们希望能有其他参数可以驱动细化。SSE不能适用于所有数据;比如说,兴趣点显示时可能需要计算标签碰撞因子和on/off distances。注意视角高于地面的高度在三维空间中不是一个好的指标,因为三维空间支持任意角度的视角。

参考#15.

矢量数据瓦片之间的裂缝应该如何处理?

三维不同于二维,相邻的瓦片可能是不同的细节程度,比如在远处的瓦片需要更低的分辨率。相邻瓦片的不同细节程度可能导致人为裂隙出现在瓦片之间。对于地形,解决方法是想边缘的地形成一定角度的向外向下延伸一点,借此填补裂缝。对于三维建筑,解决方法是将边缘建筑物完全包括在其中一个瓦片中,参考上文。对于矢量数据,这将是一个待解决的开放研究话题。这个问题可能涉及到基于边界感知的简化和实时缝合。

当使用替换细化时,多个子瓦片可以合并成一个请求吗?

通常在进行替换细化时,所有子瓦片下载完成后才会渲染(除非是像点云数据这样没有组织结构的数据,切面可以在子瓦片载入时剔除部分父瓦片;和地形的方法一样,否则父子瓦片之间任意的三维模型可能会出现裂缝或者其他错误)。

我们也许会设计出支持3D Tiles将所有子瓦片下载到一个请求中的方法,允许tileset.json指向一个文件的子集来获取瓦片内容,就像glTF中的bufferbufferView一样。HTTP/2 也会使得多请求的开销不那么大。

参考#9

如何优化叠加细化?

相比替换细化,叠加细化在大小上有优势,因为它不需要复制原始数据集中的数据。然而缺点也是有的,比如当有很多瓦片在根瓦片附近,视角放大后就需要渲染整个根瓦片,但是可能只有一个要素是可见的。

3D
Tiles能在每个瓦片中额外存储一个空间数据结构来优化这个问题。举个例子,一个瓦片中有一个2x2的格网,如果瓦片的边界体不是完全在视锥体之内的话,格网中每个单元会与视锥体进行一次计算,只有在视锥体中或者相交的单元会被渲染。

参考#11

3D Tiles使用哪种纹理的压缩格式?

3D
Tiles会使用与glTF中相同的纹理压缩格式。此外,我们需要比较GPU格式压缩和jpeg格式的优劣。有一些桌面端游戏引擎流化jpeg,然后在线程中解压后,再重新压缩成GPU格式。但是这种方法下,GPU的载荷对于JavaScript和网络端工具来说还是太高了。

致谢

数据来源

规范中的截图中,建筑物来自 CyberCity3D ,地图底图来自Bing Maps 。

浙ICP备16041529号-1