一篇19年整理的比较老的笔记了。更多精彩内容尽在数字孪生平台。
瓦片种类
3DTiles瓦片有多种类型:
b3dm(Batched 3D Model,批量3D模型)
b3dm瓦片存储了多个个体,b3dm中的glb代表的实际对象应该具有相同的种类但是可能数据内容不同。b3dm瓦片可以用于存储带纹理的地形表面、三维建筑物的外部和内部、大量的模型等。对于带纹理的地形表面,由于没有要素化的特征,所以瓦片不带有属性数据。而对于复杂建筑物的外表和内部结构(BIM数据)以及海量的三维模型数据,由于具有要素化的特征,所以需要在瓦片数据中增加批量模型的id以区分要素。从这里看出,b3dm瓦片的应用范围在空间尺度上看并没有约束,核心应用原则是针对数量巨大的要素部件,并且这与要素表达的物体类型无关。
b3dm的基本文件结构是文件头部加主体。文件头部记录了文件类型、版本、大小等基本信息,同时也记录了要素表和批量表的大小。主体部分首先是要素表与批量表,然后是存储模型的glb文件。要素表存储要素的元数据(要素个数),批量表存储具体属性(要素id等),要素表与批量表可以采用JSON文本或二进制,没有强制要求。glb文件被内嵌到b3dm文件中,其中使用的几何数据和图片资源可以集成到glb,也可以采用外部引用形式。b3dm是3DTiles中用途最多、使用最频繁的瓦片数据种类,b3dm对于“批量”的定义需要被客观精确的理解。
i3dm(Instanced 3D Model,实例化3D模型)
i3dm瓦片也存储glb的模型,但是i3dm瓦片强调物体的可复制性。这说明i3dm的模型只用于表达一个对象,通过指定位置该模型对象可以在多个位置出现。官方建议,i3dm可以用于存储大尺度上的树木、风车、消防栓、井盖、路灯、信号灯等物体,也可以用于存储小尺度上的螺丝钉、阀门、插座等。i3dm体现的是要素的相同性,这种相同性不仅表现在要素的种类,也表现在具体的内容。i3dm从很大程度上减少了数据冗余度,这也刚好诠释了i3dm对于“实例”的含义。i3dm文件的基本结构与b3dm类似,如下图所示,文件的头部仅仅多了对引用glb文件的描述(url或内嵌二进制glTF)。i3dm与b3dm的差别主要在要素表部分,i3dm的要素表除了实例的个数还需要存储实例出现的位置以及方位,通过位置坐标与方向参数确定实例模型在三维椭球上的渲染位置与姿态。此外,i3dm也提供了在局部地表的ENU坐标框架,当设置这种坐标框架开启时可以不用设置模型方位参数,而改由Cesium运行环境计算所需参数。
数据组织文件tileset.json
3DTiels用tileset.json来组织所有瓦片,它用JSON对象的形式记录了对整个树状结构的描述。
tileset.json文件顶层结构有四个属性:asset,properties,geometricError和root。asset存储瓦片集元数据信息;properties存储要素属性信息的极值;geometricError是一个非负数值,定义瓦片集的渲染条件;root存储根瓦片的信息,其中包含的属性有:
boundingVolume.region
属性是包含六个元素的数组对象,用经纬度定义了瓦片的包围盒,格式是[west, south, east, north, minimum height, maximum height]。也就是西南东北经纬度极值和高度极值的组合。经纬度以弧度为单位,高度以米为单位,除了region,还有其他包围盒格式可以使用,比如box和sphere,box是用米制单位的长宽高定义的长方体包围盒,sphere是用球心和半径定义的球形包围盒。
viewerRequestVolume
是一个可选参数。它定义了一个包围盒,当瓦片中存在viewerRequestVolume
时,只有在视口位置完全包含在viewerRequestVolume
中,瓦片内容才会被渲染。
geometricError
属性是一个非负数值,定义了一个以米为单位的几何误差,在当前瓦片已经渲染而它的子瓦片还未渲染的情况下使用。在应用运行时,几何误差用于计算屏幕空间误差(Screen-Space Error,SSE),也就是以像素为单位的误差。SSE决定HLOD层级是否应该细化。例如,判断一个瓦片在当前视图中是否足够精细,或者是否需要考虑加载它的子瓦片。
refine
属性是一个字符串,定义了细化方法。它有两个值,一个是REPLACE
,代表替换细化,也就是子瓦片替换父瓦片进行渲染,场景会删除父瓦片的内容;另一个是ADD
,用于叠加细化,也就是子瓦片叠加到场景中,细化后父瓦片依然存在。在一个tileset.json文件中的根瓦片必须要有refine
属性。其他子瓦片中如果没有refine
属性时,就继承父对象的细化方法。
content
属性是一个包含瓦片内容和链接的元数据对象。content.url
是一个绝对或者相对路径,指向瓦片的内容。content.url
中的文件后缀名定义了瓦片格式。url
可以是另一个tileset.json
文件,也就是将该瓦片的内容指向另一个3DTiles数据集。
content.boundingVolume
定义了另一个包围盒,与瓦片的boundingVolume
属性相似。但是不同于瓦片的boundingVolume
,content.boundingVolume
是一个正好紧紧包围瓦片内容的包围盒。这样的设定用于替换细化:boundingVolume
提供空间连续性,content.boundingVolume
有助于紧凑的视锥体剔除。
瓦片索引机制自由
3DTiles没有强制要求瓦片索引树的具体结构,对瓦片空间的划分方式上也没有强制定义,仅仅要求瓦片所占据的空间在整个瓦片索引树结构中要保持空间一致性。
对于同样的地理空间,k-d树具有最深的分割层次,四叉树和八叉树次之,格网最浅。其中,格网索引结构对Cesium的压力最大,因为所有的子节点都在根节点下,Cesium每一次对包围盒的检查都要遍历所有结点,所以格网适合分块较少的情况。k-d树对于Cesium的要求较小,但是每一次划分都不规则,分割的标准不好确定。所以,一般情况下都是用折中的四叉树结构或八叉树结构。由于八叉树多用于构建点云索引,所以对于3DTiles来说四叉树是地表空间附近常用的索引方案。
对象映射机制自由
Cesium中所有三维模型的渲染最终都是以primitive图元为单位提交绘制命令的,图元中包含的数据有几何数据以及材质引用的纹理。Cesium研发团队为了在WebGL基础上实现要素拾取和属性查找的功能,对3DTiles瓦片中的gltf模型增加了新的内容:在primitive的attitude中增加了BATCHID属性,BATCHID属性与顶点数据类似,通过访问器连接到特定的二进制数据。BATCHID存储的是整数数组,这个数组与顶点数组元素一一对应,也就是说每一个顶点都被打上了一个整数id的标记。Cesium在实现要素拾取时,只要拾取到渲染的图元中的三角形就可以得到该三角形的任意一个顶点,也就可以获取每个顶点对应的BATCHID整数。然后由这个整数值在batchtable的属性数组中找到对应索引位置的属性数据。
b3dm使用的glb中_BATCHID整数通常采用UNSIGNED_SHORT类型(数值范围0255),所以一般单个瓦片中的要素数量不超过256个。当某些时候要素数量超过这个值时,那么在生产数据时_BATCHID整数的类型要调整为UNSIGNED_INT(数值范围04294967295),但这种情况很少发生。下图是要素映射机制的图例。
这种特殊的对象映射机制具有一种非常强大的优势:可以完全屏蔽掉gltf定义对要素的组织方式。也就是说,不论gltf根据自己的定义框架将待描述物体组织成什么结构,当gltf被包装成b3dm瓦片时依然可以对要素进行区分。这个方法跳过了gltf这一层数据框架的限制,使那些采用不同数据组织逻辑转化出的gltf数据都可以实现要素化。这样自由的要素映射机制使各种异质数据更加容易转化,体现了3DTiles的兼容性。但是从数据生产上游角度来看,在gltf转化成3DTiles之前,仍然要明确要素在gltf中的结构。只有这样对_BATCHID数据的构造才是符合逻辑的。
位置变换机制多样
在3DTiles中有很多设置瓦片坐标的变换方法:
- 通过瓦片集索引树中的transform属性设置矩阵
- 通过瓦片内gltf的节点矩阵设置
- 通过
CESIUM_RTC
扩展来添加平移量
一般情况下转换为3DTiles的数据源模型都在一个局部坐标框架下。以ENU坐标系为例,在这个坐标系下,空间的正东、正北以及上坐标都是确定的,坐标原点对应着在椭球上的地理位置,这样模型存储在这个坐标系中的位置也是固定的。那么,为了将局部坐标系的模型放置在椭球表面时,需要的工作就是根据函数Transforms.eastNorthUpToFixedFrame计算坐标变换矩阵M,并把坐标变换矩阵M配置到3DTiles数据中。在Cesium上直接加载gltf的API就是根据计算出的矩阵M将gltf模型放置在椭球表面上的。由于设置变换矩阵有很多方法,所以配置矩阵就有很多方案。第一种方案,将矩阵M直接配置在瓦片元数据的transform属性中;第二种方案,将矩阵M配置到gltf的根节点中;第三种方案,将矩阵M分解为平移矩阵T和旋转矩阵R,即M=TR,将旋转矩阵R配置给gltf的根节点矩阵,将矩阵T中平移参数作为平移分量设置为gltf定义的扩展设置CESIUM_RTC;第四种方案,将M分解的平移矩阵T分量配置给CESIUM_RTC,将R矩阵作用于顶点和外法向量数据,直接改动模型底层的顶点数据。
LOD配置多样
3DTiles瓦片精化方式有两种:REPLACE和ADD。REPLACE就是用子节点中更加精细的模型代替父节点中略显粗糙的模型,各层数据可以通过最精细模型采用自动简化算法获得。ADD则提供了另外一种不同的思路:在父亲节点的模型基础上再增加一些相对次要的模型,从而使三维场景包含更多的信息。下图是两种方式的对比图:
在大空间范围内的海量建筑物显示以及小空间范围内的精细复杂模型的显示都可以用合适的精化方式来表示。例如,大范围的倾斜摄影OSGB数据本身带有LOD,可以很容易的转成REPLACE精化方式下的3DTiles数据。又如,通过OSM数据生产的纽约城市建筑物3DTiles,是采用ADD精化方式组织的建筑物瓦片,加载时先加载节点树中顶层的模型,然后根据区域逐层添加次要建筑。此外,在BIM数据的表现方面3DTiles的精化逻辑也可以完美的适应,根据数据源的基本结构也可以采用ADD精化方式组织BIM模型部件。除了这些常用的LOD方式,ADD和REPLACE方式也可以混合使用,这样生产的3DTiles数据会具有更加优化的显示效果。
属性数据配置多样
关于属性数据配置有多种方案:
(1)项目应用仅仅为了显示,与要素和属性无关。在这种情况下,3DTiles只需要储存模型数据,所以b3dm瓦片中的batchTable批量表为空,内嵌glb中的_BATCHID数据不需要。这种情况很多,比如加载显示倾斜摄影模型或三维地形等。
(2)项目应用需要存储要素与属性。此时内嵌gltf需要_BATCHID数据,但batchTable批量表就存在两种可选的方案。一种是将属性数据全部存储到批量表中,与模型数据集中管理。另一种方案是属性数据存储到数据库中,批量表中只存储要素ID,使属性和模型分离。在应用开发时由Cesium运行环境获取要素ID,然后根据ID调用属性查询服务获取更加详细的属性信息。