Cesium是一个虚拟地球三维平台,可视化范围上至太空中每一颗卫星,下至地面上每一幢建筑物。为了实现数字地球(Digital Earth vision)的蓝图,使连接世界上的地理空间数据成为可能,就要用到3D-Tiles。
3D-Tiles是一个用于流式(stream)传输大规模、异构的三维空间数据集的开放规范(open specification)。为了在Cesium地形和影像成流技术的基础上拓展功能,需要用3D Tiles成流三维数据,包括建筑物,树,点云和矢量数据。
简单点说,3D Tiles是在gltf的基础上,加入了分层LOD的结构后得到的产品,专门为大量地理3D数据流式传输和海量渲染而设计的一种格式,是webGL框架Cesium的专用格式。
主要有以下几个特点:
Open
3D Tiles是一个开放式规范,在Cesium中具有开源实现。
Optimized for streaming and rendering(针对流和渲染进行了优化)
3D Tiles主要是对大规模异构数据集的成流和渲染进行优化。3D Tiles的基础是一种空间数据结构,它支持层次结构细节级别(HLOD),只有可见的图块才会被流式传输。
Interactive(交互式)
3D Tiles支持交互式选择和样式,可以单独进行模型交互。比如鼠标悬停显示建筑物、使用ID查询数据。
Styleable(设置样式)
单个模型的元数据可以在运行时用于着色而无需编写代码,样式可以即时更改。
Adaptable(适应性)
为了满足灵活性的需求,树可以是任何具有空间相干性的空间数据结构,包括k-d树,四叉树(quadtrees),八叉树(octrees),格网(grids)。
Flexible(灵活)
传统的2D地图图块,当用户放大时,可见的地图图块将被更高分辨率的地图图块替换,这称为细化。
而3D数据集则可以在子图块下载时呈现,这称为添加剂细化,具有更大的灵活性。
Heterogeneous(异构的)
3D数据集没有可以适合所有的尺寸,批量模型需要来自实例模型的不同表示,以及来自点云的不同表示等。
3D Tiles通过启用自适应细分,灵活细化和可扩展的切片格式集支持异构数据集。
Precise(精确)
3D Tiles提供全精度几何,避免抖动伪像,无需存储双精度值。
Temporal(时间动态)
Cesium专为时间动态可视化设计,例如卫星、无人机。
3D Tiles数据集(又称为tileset)是由一系列tile组成的树状结构。每一个tile都有一个包围体完全包围它的内容(content)。树具有空间相关性,子tile的内容完全包含在父tile的包围体内。
一个tile代表一个要素或者一个要素集,如建筑物为代表的3D模型、点云中的点和向量数据集中的点。每个tile可以引用以下四种格式中的一种:
tile的内容(tile格式的一个单独实例)是一个二进制块,具有特定于格式的组件,包括功能表(Feature Table)和批处理表(Batch Table)。
批处理3D模型和实例3D模型格式基于glTF建立,glTF是为有效传输3D内容而设计的开放规范。这些格式的图块内容在二进制主体中嵌入了glTF资源,其中包含模型几何和纹理信息。点云格式未嵌入glTF。
tile以树形结构组织,其中结合了详细层次结构(HLOD)的概念,可最佳呈现空间数据。每个tile都有一个包围体积(bounding volume)属性,一个对象定义了一个完全包围其内容的空间范围。树具有空间连贯性;子tile的内容完全在父级的包围体积之内。
树可以是任何具有空间相干性的空间数据结构,包括k-d树,四叉树(quadtrees),八叉树(octrees),格网(grids)
JSON必须使用没有BOM的UTF-8编码。
所有字符串(属性名称,枚举)仅使用ASCII字符集,并且必须以纯文本形式编写。
JSON对象中的名称(键)必须唯一,即不允许重复的键。
所有线性距离的单位是米。
所有角度均以弧度为单位
根节点是源几何图形的最简化版本,是最粗糙的模型,其几何误差最大。然后,每个连续级别的子级将具有比其父级低的几何误差,而叶子节点的几何误差为0或接近0。
在不同视距、不同视角、不同分辨率下,几何误差都是不同的,几何误差是根据度量标准制定的。通常,较高的几何误差表示将更快的优化tile,并且将更快地加载和渲染子tile。
细化确定了较低分辨率的父tile被选择渲染的子级时渲染的过程。 允许的细化类型为替换(“ REPLACE”)和添加剂(“ ADD”)。
如果tile具有替换细化,则将渲染子tile代替父tile,即不再渲染父tile,即REPLACE是在Tile从低Lod到高Lod时,将低Lod的数据直接移除,用高Lod的数据替换。 如果tile具有添加剂细化,则除了父tile之外,还将渲染子代。
ADD方式是一种非常好的方式,是一种增量的LOD策略,能减少数据的传输
简单点理解是能将三维对象完全包住的最小的几何体。3D Tiles有三种包围体:
Bounding box:box属性是一个由12个数字组成的数组。 前三个元素定义框中心的x,y和z值。 接下来的三组元素分别定义x,y,z轴方向和半轴长度
Bounding region:region属性是一个由六个数字组成的数组,这些数字用纬度,经度和高度坐标定义了地理区域,其坐标顺序为[西,南,东,北,最小高度,最大高度]。 纬度和经度在EPSG 4979中定义的WGS 84基准中,以弧度表示。 高度在WGS 84椭圆形上方(或下方)以米为单位。其每条边都和坐标轴平行。
Bounding sphere:sphere属性是由四个数字组成的数组,这些数字定义了一个最小包围球。 前三个元素定义了在右手3轴(x,y,z)直角坐标系中x轴的x,y和z值,其中z轴位于上方。 最后一个元素以米为单位定义半径。
蓝色是region包围框,每条边都和坐标轴平行;红色的是box包围框,是能包围三维对象的最小几何体。
这个是对每个tile何时可见进行控制,它也包含了上面的box,region,sphere三种类型。当观察点在viewerRequestVolume值内的时候,tile才会显示,这样更有利于精确控制tile的可见性。
为了支持局部坐标系,给每个tile都提供了一个可选的transform属性。
transform属性是一个4x4变换矩阵,按列优先顺序存储,可从tile的本地坐标系转换到父节点的坐标系中,或者在根节点的情况下从tileset的坐标系转换.
对tile的变化是从上自下多个变换的一个级联变换的过程,因为LOD是一个树的结构,所以叶子节点的变换就是从根节点到下的一个矩阵级联变换的过程。
content属性用来指向Tile实际渲染的内容。content.uri指向渲染内容的定位符,可能指向一个二进制块的位置,也可能指向另一个Tileset。content.uri不需要文件扩展名。内容的tile格式可以通过其标题中的magic字段标识,如果内容为JSON,则可以将其标识为外部tileset.
content.boundingVolume属性用来所指向描述渲染内容的包围体,其与tile.boundingVolume的区别是,content属性只是渲染内容的包围体,而tile需要将子节点包围在里面。
红色的是tile的包围体,蓝色的是content的包围体。
因为3DTiles是以树结构组织的,每个Tile还有子节点,就存储在children数组中,数组中每个元素仍然是Tile,这样就形成了一种递归定义的树的结构,叶子节点的children的元素个数为0。需要注意是,子节点的boudingVolume肯定是被父节点的boudingVolume包围着的,子节点的geometricError肯定是要比父节点小的,因为越接近叶子节点,模型越精细,所以其与原模型的几何误差越小。
一个tileJSON对象由以下属性组成:
每个属性的作用总结为下:
- boudingVolume:boudingVolume定义了Tile的最小包围体,其作用是在渲染的时候,根据包围体确定哪个Tile需要渲染的。其有region,box,sphere三种形式。
- geometricError:geometricError是一个非负数,以米为单位定义了不同LOD层级(或者说Tile层级)的几何误差,通过几何误差来计算屏幕误差,从而确定什么时候应该用哪个LOD层级的Tile。
- viewerRequestVolume:viewerRequestVolume用一个和boudingVolume相同的类型定义的范围,只有当观察者处于其定义的范围内时,Tile才显示,从而精细控制了tile的显示与否。
- refine:refine属性定义了tile切换的方式,有替换和添加两种模式。该属性在根节点的Tile中是必须的,子节点中非必须,子节点中该属性缺失时,继承父节点的该属性。
- content:此属性指向真正的渲染数据。
- children:定义子节点的对象数组。
3D Tiles使用一个或者多个tileset的json文件来组成整个场景,这些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
asset是一个对象,其中包含有关整个tileset的元数据。 asset.version属性是一个字符串,用于定义3D Tiles版本,该版本指定tileet的JSON模式和基本的tile格式集。 tileetVersion属性是一个可选字符串,用于定义特定于应用程序的tileset版本。
- properties
该属性中包含tileset中每个功能属性的对象。此tileset JSON代码段适用于3D建筑物,因此每个tile均具有建筑物模型,并且每个建筑物模型均具有Height属性。属性中每个对象的名称与每个功能属性的名称匹配,并且其值定义其最小和最大数值,这对于创建用于样式的色带很有用。
- geometricError
geometricError是一个非负数,用于定义未渲染图块时的误差(以米为单位),用于确定是否渲染tile的元数据。tile也有geometricError这个属性,不同的是,tileSet的geometricError是根据屏幕误差来控制tileSet中的root是否渲染。而tile中的geometricError则是用来控制tile中的children是否渲染。
- root
tileet的geometricError是未渲染整个tileset时的误差; root.geometricError是仅渲染根节点时的误差。
要创建一棵树,tile的content.uri可以指向外部tileset(另一个图块JSON文件的uri)。这样可以把不同的tileset分开存储,比如一个国家的模型,可以将每个城市存储在一个tileset 中,然后用一个具有全局的tileset的JSON文件将整个国家组织起来。
当一个tile指向一个外部的tileset时,必需遵循一下原则:
不能有子集; tile.children必须未定义或为空数组。
不能用于创建循环,例如有set1、set2、set3三个tileSet,set1指向set2,set2指向set3,然后set3又指向set1,这就形成一个引用环,这种情况是不允许的。
利用transform变换的时候,将会应用自己的transform变换和root的transform变换。 例如,在以下引用中,针对T3的计算转换为[T0] [T1] [T2] [T3]。
如上所述,树具有空间连贯性。每个tile都有一个完全包围其内容的包围体,子节点包围体内的模型内容(是模型内容而不是子节点的包围体)应完全被父节点的包围体所包围,但这并意味着children的包围体要完全被父节点的包围体包围。例如:
四个子节点的包围体。子对象的内容完全在父对象的包围体内,但子对象的包围体不在此范围内。
3DTiles是一种利用HLOD来组织场景的,tileSet内的数据结构是一个树形的结构(在tileSet的JSON文件中,通过root和children来递归的定义),可以通过不同类型的空间数据结构进行组织。
当每个Tile具有四个统一细分的子级(例如,使用中心纬度和经度)时,就会创建四叉树,类似于典型的2D地理空间切片方案。空的子tile可以省略。
3D Tiles支持非规则的四叉树,例如用非均匀细分和紧密的包围体来划分。
这是一个实例,注意左下角,其中包围体不包括左侧的水,此处没有建筑物:
3DTiles同样支持松散四叉树,这种情况下,子节点之间可能会出现压盖,但是包围体的连贯性还是需要保持(即父节点的包围体必须要将子节点的数据包含在里面)。使用松散四叉树分割,就可以割裂建筑物。松散四叉树的示意图如下:
松散四叉树的实际应用如下图所示,可以看到中间有两个包围体是有压盖的。这样与包围体相交的两个建筑物就不需要分割开了。
当每个节点有两个子节点时,会创建k-d树,这两个子节点由平行于x、y或z轴(或纬度、经度、高度)的分割平面分隔开。分割轴通常随着树的层级增加而循环旋转,并且分割平面可以使用中间分割、表面积分割或其他方法来拆分。
树的每个节点代表一个超平面,该超平面垂直于当前划分维度的坐标轴,并在该维度上将空间划分为两部分,一部分在其左子树,另一部分在其右子树;即若当前节点的划分维度为i,其左子树上所有点在i维的值均小于当前值,右子树上所有点在i维的值均大于等于当前值,本定义对其任意子节点均成立。一个k=2的k-d树如下所示:
请注意,k-d树不像典型的2D地理空间切片方案那样具有统一的细分,因此可以为稀疏和非均匀分布的数据集创建更加平衡的树,每个tile有n个孩子,而不是每个tile有2个孩子。
八叉树通过使用三个正交拆分平面将tile细分为八个子级来扩展四叉树。像四叉树一样,3D Tiles允许对八叉树进行变化,例如不均匀的细分,紧密的包围体和重叠的子代。如下为八叉图分割示例:
3DTiles支持任意数量的子节点来实现均匀和非均匀重叠的网格。 例如,以下是剑桥的非均匀重叠网格的俯视图:
总体来说,3D Tiles就是定义了一种用递归构造的树的数据结构,树怎么形成可以由用户自定义。其次,3D Tiles也给每个tile定义了诸如几何误差等的属性,用于控制tile的显示与否。
3DTiles规范原文点这里!