原文地址:https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.mdhtml
前言git
在3D Tiles中,一個砌塊集是一系列以樹型空間數據結構組織起來的砌塊。每個砌塊都有一個徹底包裹它所有內容的包圍體。樹型空間數據結構具備空間關係;全部子砌塊的內容都徹底包含在父砌塊的包圍體中。爲了保證靈活性,樹能夠是任何具備空間關係的空間數據結構,例如K-D樹、四叉樹、八叉樹、格網。github
爲了支持對從規則劃分的地形到零散分佈的城市再到無序點雲等各類各樣的數據集的緊密包裹,包圍體多是個定向的包圍盒或包圍球或者由最大和最小經度、緯度、高程所定義的地理區域。算法
一個砌塊索引一個或一組要素,例如以建築物或綠化爲主的三維模型,點雲中的點,多邊形,折線,還有矢量數據集中的點。這些要素能夠分批組合成一個對象以減小客戶端的加載時間和WebGL繪製函數的調用開銷。json
砌塊元數據數組
每一個砌塊的元數據(不是數據自己)以JSON格式定義。例如:數據結構
{app
"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這個屬性是個包含六個數的數組,以 [最西、最南、最東、最北、最小高程、最大高程] 的順序定義所包圍的地理區域。其中經度和緯度以弧度爲單位,高程是高於(或低於)WGS84橢球體的米數。除了區域外,其餘包圍體例如盒子和球體也可能會用到。
geometricError這個屬性以一個以米爲單位的非負數字定義了「尺度(error、分辨率)」。引進這一參數用於界定當一個砌塊已經被渲染而它的子砌塊未被渲染。在調度過程當中,幾何尺度參與計算以像素爲單位計量的屏幕空間尺度(SSE)。屏幕空間尺度決定着分層層次細節模型(HLOD)的更新,也就是在當前視野下是否成功加載精細的砌塊或者這個砌塊的子砌塊是否須要預取。
viewerRequestVolume是個可選的屬性(這個例子中沒有),使用和boundingVolume一樣的結構定義了一個體。在砌塊內容將要被請求和砌塊將要根據geometricError更新以前,viewer(可視空間)必須在這個體中。參看Viewer request volume(可視空間請求體)部分。
refine屬性是一個字符串,當值爲「replace」時爲替換型更新,值爲「add」時爲累加型更新。對於一個砌塊數據集的根節點來講這個屬性是必須的,而對於其餘砌塊來講這是個可選屬性。refine屬性默認將從砌塊的父節點繼承。
content屬性是一個對象,對象中包含砌塊數據的元數據和數據的地址。content.url的值是一個字符串,這個字符串指向砌塊數據的絕對或相對url。在上面的示例中,2/0/0.b3dm這個url具備瓦片地圖服務的命名規則即「{z}/{y}/{x}.擴展名」,這並非必須的。參看答疑「如何請求第n級瓦片?」。
content.url屬性中文件的擴展名定義了砌塊格式,這一url還能夠經過一個tileset.json文件建立一個砌塊集的子集,參看 外部砌塊集 部分。
content.boundingVolume屬性定義了與頂級boundingVolume屬性相似的可選的包圍體,但與其不一樣的是content.boundingVolume是一個僅包含砌塊內容的緊密貼合的包圍體,是用於取代更新部分的。boundingVolume屬性提供了空間關係 ,content.boundingVolume屬性實現了嚴格的視錐體裁剪。下面的截圖展現了金絲雀碼頭示例中根節點的包圍體。boundingVolume以紅色線框表示,包裹砌塊集的整個區域;content.boundingVolume以藍色線框表示,僅包裹根節點中的四個對象(模型)。
content屬性是可選的,當其未定義時,砌塊的包圍體仍會被用於裁剪。(參看 格網 部分)
transform屬性也是可選的(本例中沒有),它定義了一個4x4的仿射變換矩陣將砌塊的scontent
、boundingVolume
、
viewerRequestVolume
轉換成「砌塊變換」部分所描述的形式。
children屬性是一個對象數組,其中定義了子節點,參看tileset.json部分
座標系與單位
3D Tiles中採用了右手笛卡爾座標系,即x與y的向量積是z。3D Tiles中定義z軸爲局部笛卡爾座標系中向上的方向(參看 砌塊變換 部分)。一個砌塊集的全局座標系一般是WGS84的,但這並非必須的,好比使用了沒有地理空間參考的建模工具後一座電廠可能徹底定義在它的地方座標系下。
全部的直線距離單位都是米,全部的角度單位都是弧度。
3D Tiles中並不明文存儲地理座標(精度、緯度、高程),地理座標能夠由WGS84座標計算獲得,由於WGS84座標不須要非仿射座標轉換,使用WGS84座標能夠提升GPU的渲染效率。3D Tiles砌塊集能夠包含專用的元數據,例如地理座標,但這並非3D Tiles規格的一部分。
砌塊轉換
砌塊轉換目的是支持地方座標系,好比使得城市砌塊集內的某個建築物砌塊集能夠定義在它本身的座標系統下,再好比建築物點雲中的點雲塊也能夠定義在它本身的座標系統下,每個砌塊都有可選的transform屬性。
transform屬性是個按照列順序存儲的4x4的仿射變換矩陣,這一變換將砌塊的地方座標系轉換到它的父節點或根節點的座標系。
transform屬性應用於以下情形(對象):
transform
的逆轉置矩陣左上角的3x3矩陣轉換,參看 尺度變化時糾正矢量旋轉。transform屬性與geometricError屬性沒有關係,好比Transform屬性中定義的尺度並不會決定幾何尺度的大小;幾何尺度始終以米爲單位。
當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
]
每個砌塊的局部座標向砌塊集全局座標系的轉換都是由砌塊集從上到下的遍歷計算,正如計算機圖形中傳統的場景圖或者節點層次那樣,將砌塊的transform矩陣乘在它的父節點的transform矩陣右邊。
下面的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 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);
}
}
以下是一個砌塊集的轉換矩陣計算的例子(上面代碼中的transformToRoot):
每一個砌塊變換矩陣的計算式爲:
l T0:[T0]
l T1:[T0][T1]
l T2:[T0][T2]
l T3:[T0][T1][T3]
l T4:[T0][T1][T4]
在定義變換矩陣或仿射變換右乘以前,砌塊內容裏或許已經有砌塊專屬的適用於位置和法線的變換,示例以下:
l 因爲glTF中定義了本身的節點層級關係,對於內嵌glTF的b3dm和i3dm砌塊,每個節點都有變換矩陣,這會優先於tile.transform中的定義。
l i3dm的要素表中定義了位置、法線和縮放尺度的實例,這些數據能夠爲每一個實例生成4x4的仿射變換矩陣,這會優於tile.transform屬性應用於每一個實例。
l 像POSITION_QUANTIZED這種在i3dm、 pnts和vctr的要素表中被壓縮的屬性應該在作任何變換以前解壓,pnts中的NORMAL_OCT16P也是這樣。
所以,上面例子的完整變換矩陣的計算式應爲:
l T0:[T0]
l T1:[T0][T1]
l T2:[T0][T2][源自ptn專有要素表屬性派生的變換矩陣]
l T3:[T0][T1][T3][b3dm專有變換矩陣(含glTF節點層級關係)]
l T4:[T0][T1][T4][i3dm專有變換矩陣(含每一個屬性要素表屬性派生的變換矩陣和glTF節點層級關係)]
可視空間請求體
一個砌塊的viewerRequestVolume可用於與異構數據和外部砌塊集的結合。
下面的示例中有一個在b3dm砌塊中的建築物,建築物中有一塊pnts砌塊中的點雲。點雲砌塊的boundingVolume是個半徑爲1.25的球體,它還有個較大的球體做爲ViewerRequestVolume,球體的半徑是15。由於geometricError的值是0,在可視空間進入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"
}
}]
備忘:插入數據請求體與包圍體對比的截圖
tileset.json
tileset.json定義了切片集,下面是金絲雀碼頭中使用的tileset.json的片斷(查看完整版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是一個包含整個切片集元數據屬性的對象。其中的version屬性以字符串形式定義了3D Tiles的版本。版本定義了tileset.json的JSON模式和砌塊格式的基本集。tilesetVersion屬性是可選的,它定義了一個專用的版本號,用於相似當前砌塊集升級這樣的狀況。
properties是一個包含每個原始要素屬性對象的對象。上面的tileset.json片斷是針對三維建築物的,因此每一個砌塊都含有建築物模型,每一個建築物模型都有Height屬性(參看[TileFormats/BatchTable/README.md]中的「Batch Table」)。properties屬性中每個對象的名字與原始對象中的名字相對應並定義了它的minimum和
maximum值,當在爲樣式生成色帶這樣的應用時這個屬性是有用的。
geometricError以一個以米爲單位的非負數字定義了尺度,在這個尺度下切片集不會被渲染。
root是一個定義了在砌塊元數據中描述的JSON所定義的根砌塊的對象。root.geometricError與 tileset.json中頂層的geometricError不一樣,tileset.json的geometricError是整個砌塊集不被渲染的尺度,root.geometricError是隻有根節點砌塊被渲染的尺度。
root.children是一個定義了子砌塊的對象數組。每個子砌塊都有被其父砌塊包圍體所徹底包裹的boundingVolume,並且在一般狀況下一個砌塊的geometricError要小於其父砌塊的geometricError。對於葉子砌塊,root.children數組的長度是0,children可能未定義。
關於tileset.json的詳細JSON數據模式請參看「3D Tiles的JSON數據模式」。
參看問題「tileset.json是否會加入3D Tiles細則?」瞭解tileset.json 如何擴展海量砌塊。
外部砌塊集
爲實如今樹的分支下建立子樹,砌塊的content.url屬性能夠指向一個外部砌塊集(另外一個tileset.json)。這樣能夠實現諸如將每一個城市保存在一個砌塊集中,這些砌塊集再構成一個全局砌塊集的狀況:
當一個砌塊指向了一個外部砌塊集,這個砌塊應該:
包圍體空間關係
正如上面描述的那樣,樹結構具備空間相關性;每一個砌塊都有包圍體徹底包裹它的內容,並且子砌塊的內容徹底在父砌塊包圍體內部。這並非說子砌塊的包圍體要徹底在父砌塊的包圍體內部,例以下圖:
地形瓦片的包圍球
四個子瓦片的包圍球。子瓦片的內容徹底在父瓦片的包圍體內,但子瓦片的包圍體並不在父瓦片的包圍體內,它們並非嚴密貼合的。
建立空間數據結構
tileset.json中定義的樹由root和它的children遞歸構成,樹能夠定義不一樣種類的空間數據結構。除此以外,任何砌塊格式和更新策略(替換或增長)的組合均可以使用,這給對異構數據的支持提供了不少便利。
生成tileset.json的轉換工具將爲數據集定義一種理想的樹。一個像Cesium這樣的實時運行引擎能夠渲染任何由tileset.json定義的樹。如下是一個關於3D Tiles如何表達各類各樣的空間數據結構的簡要說明。
K-d 樹
當每一個砌塊有兩個被平行於x、y或z軸(或精度、緯度、高程)的分割面分開的子節點時,k-d樹就能夠被建立。分割軸一般隨着樹的深度的增長循環旋轉,分割面能夠用取中點劃分、表面啓發式劃分或其餘途徑選出。
k-d樹示例。注意分割是不均勻的。
須要注意的是k-d樹並不像典型的二維地理空間切片算法那樣規則分割,所以k-d樹能夠爲稀疏和不均勻分佈的數據集建立更加和諧的樹型結構。
3D Tiles還支持k-d樹的變種,例如多路k-d樹,在樹的每個葉子節點上有沿着座標軸的多個分割,每個砌塊有n個子節點而不是兩個。
四叉樹
當一個砌塊能夠分割成統一的四個子節點,四叉樹就能夠被建立(例如使用中央經緯度分割)。空的子砌塊會像典型的二維空間切片算法中那樣被忽略。
經典四叉樹分割
3D Tiles支持四叉樹的變種,例如不均勻分割和緊密包圍體(與包圍框相反,例如,對稀疏數據集來講包圍框父節點有25%的浪費)。
每一個子節點都有緊密包圍體的四叉樹
下面的例子中是金絲雀碼頭的根砌塊和它的子砌塊。注意左下角的包圍體中並不包含左側的水域,由於那個區域並無建築物。
3D Tiles 還支持其餘的四叉樹變種,好比「鬆散四叉樹」,樹的子樹重疊但空間關係得以保留,也就是父砌塊徹底包裹它全部的子砌塊。這能夠避免分割跨砌塊的要素,例如三維模型。
有不一致分割且重疊砌塊的四叉樹
下圖中,綠色的建築物處於左子砌塊中,紫色的建築物處於右子砌塊中。注意砌塊重疊的部分,中部兩個綠色的建築物和一個紫色的建築物並無分割。
八叉樹
八叉樹經過使用三個正交的分割面將一個砌塊分紅八個子砌塊擴展了四叉樹。像四叉樹同樣,3D Tiles支持八叉樹的變種,例如不規則分割、緊密包圍體和重疊子樹。
傳統八叉樹分割
累加式更新的點雲不規則八叉樹分割。法國沙佩的聖瑪麗教堂。
格網
3D Tiles 經過支持任意數量的子切片支持規則格網、不規則格網、重疊格網。下圖是劍橋市不規則重疊格網的俯視圖:
3D Tiles會利用那些有包圍體但沒有內容空砌塊。既然空砌塊的content沒有必要定義,空的非葉子節點經過層次剔除被用於加速不規則格網。這在本質上建立了一個沒有分層層次細節的八叉樹或四叉樹。
砌塊格式
每個砌塊的content.url屬性指向的另外一個砌塊的格式都在上面的格式支持表中列了出來。
一個砌塊集能夠包含任意砌塊格式的組合。3D Tiles 可能也會在同一個砌塊中經過使用複合砌塊支持不一樣的格式。
聲明式樣式
使用聲明式樣式以高度值爲建築物着色
3D Tiles包含簡潔的以JSON格式定義的聲明式樣式,表達式使用樣式擴展的JavaScript的一個小子集書寫。
樣式經過一個基於要素屬性的表達式決定要素的show和color(RGB值和透明度),例如:
{
"color" : "(${Temperature} > 90) ? color('red') : color('white')"
}
這個顏色特徵在溫度高於90時是紅色,其餘則是白色。
更多細節參看聲明式樣式部分。
答疑
普通問題
技術問題