i3dm,即 Instanced 3D Model
,實例三維模型的意思。html
諸如樹木、路燈、路邊的垃圾桶、長椅等具備明顯 重複 特徵的數據。這類數據用得較少(笑,如今都喜歡搞BIM、傾斜攝影、精模、白模等)git
個人git地址:github.com/onsummer
轉載請規範化轉載。出處:@秋意正寒 http://www.javashuo.com/article/p-qxyyfyhq-mc.htmlgithub
與 b3dm 一致,文件頭多了個屬性。json
i3dm的文件頭有8個屬性,前7個與b3dm是同樣的。佈局
屬性的官方名稱 | 字節長 | 類型 | 含義 |
---|---|---|---|
magic |
4 | string(或char[4]) | 該瓦片文件的類型,在i3dm中是 "i3dm" |
version |
4 | uint32 | 該瓦片的版本,目前限定是 1. |
byteLength |
4 | uint32 | 該瓦片文件的文件大小,單位:byte |
featureTableJSONByteLength |
4 | uint32 | 要素表的JSON文本(二進制形式)長度 |
featureTableBinaryByteLength |
4 | uint32 | 要素表的二進制數據長度 |
batchTableJSONByteLength |
4 | uint32 | 批量表的JSON文本(二進制形式)長度 |
batchTableBinaryByteLength |
4 | uint32 | 批量表的二進制數據長度 |
gltfFormat |
4 | uint32 | gltf在i3dm瓦片中存在的形式 |
其中,前7個和b3dm意義同樣,不作解釋。ui
第8個,gltfFormat
只有兩個值:0和1.編碼
0,則位於 i3dm 瓦片文件最後的 gltf 內容是一個 uri,指向gltf的數據內容(多是Base64 DataURL,也多是其餘地方的地址,筆者沒見過...)spa
1,則位於 i3dm 瓦片文件最後的 gltf 內容是 二進制的 glb,大多數狀況見的是這個。3d
默認狀況,gltf 是 y 軸朝上,3dTiles 是z軸朝上,須要座標轉換。code
在上篇,有介紹到要素表存在 全局屬性 和 要素屬性。在 i3dm 中,這對概念就能獲得很好的解釋。
屬性名 | 數據類型 | 描述 | 是否必須 |
---|---|---|---|
INSTANCES_LENGTH | uint32 | instance的個數 | 是 |
RTC_CENTER | float32[3] | 若是座標是相對座標,那麼相對中心由此屬性給出 | 否 |
QUANTIZED_VOLUME_OFFSET | float32[3] | 量化空間範圍體的偏移量 | 否,與要素屬性中的POSITION_QUANTIZED 共存亡 |
QUANTIZED_VOLUME_SCALE | float32[3] | 量化空間範圍體的縮放比例 | 否,與要素屬性中的POSITION_QUANTIZED 共存亡 |
EAST_NORTH_UP | boolean | 若是這個屬性值是true,並且每一個實例的方向沒有定義,那麼每一個實例將默認指向WGS84橢球的正東、正北方向。 | 否 |
第一第二個能與 b3dm 中的 BATCH_LENGTH
和 RTC_CENTER
類比來理解,就不解釋了。
最後一個屬性指示當前 i3dm 瓦片的座標軸朝向。
下列要着重介紹這個所謂的 QUANTIZED_VOLUME
,即 量化空間範圍體。
這個詞「量化空間範圍體」是我本身意譯的。
每一個瓦片,都有它本身的空間範圍,爲了節約數據佔用,可使用相對座標來記錄每一個 instance 的位置,也即記錄全局屬性中的 RTC_CENTER
屬性。
可是,即使用了相對座標,instance 的座標值仍然是 FLOAT
類型,佔 4字節。
假設,存在一個矩形空間,它的左下角點的座標是 (x, y, z)
,將矩形空間按 \(2^{16}\) 等分其 x、y、z 三個方向,定義矩形空間的三條邊長對應瓦片自己的座標空間的縮放比例爲 (ScaleX, ScaleY, ScaleZ)
,以下圖所示:
這樣,被細分出來的每個 「小矩形」,都有它本身在這個矩形空間的量化座標,由於 x、y、z 三個方向被分割成了 \(2^{16}\) 塊,咱們可使用 uint16
類型的數值來記錄座標,這樣每一個數字只佔了 16bit,也即 2byte,比 FLOAT
的4byte 小了一倍,對於頂點數據的壓縮十分具備價值。
那麼,如何將 (16464, 2172, 63312)
這個量化的座標映射回瓦片本來的座標呢?參考公式:
\(\vec{Position} = Scale * \displaystyle\frac{\vec{PositionQuantized}}{65535} + \vec{Offset}\)
即量化座標 PositionQuantized
各個座標份量乘上縮放因子( Scale / 65535
),而後加偏移座標便可。
三個方向的縮放因子 QUANTIZED_VOLUME_SCALE:float[3]
和 偏移量 QUANTIZED_VOLUME_OFFSET:float[3]
做爲全局屬性寫在要素表JSON中。
若是這兩個全局屬性未定義,則 逐要素屬性中的 POSITION_QUANTIZED
這個量化座標也不會存在,即便用原有的 float 類型座標記法。
須要注意的是,量化座標和普通座標只能二選一,若是都不存在,那麼這個 i3dm 瓦片就不會被渲染。
看到這,是否能理解「要素表的全局屬性是對於整個瓦片文件而言」這句話了呢?
屬性名稱 | 數據類型 | 描述 | 是否必須 |
---|---|---|---|
POSITION | float32[3] | 模型實例的座標 | 是,與POSITION_QUANTIZED二選一 |
POSITION_QUANTIZED | uint16[3] | 量化空間範圍體內的模型實例座標 | 是,與POSITION二選一 |
NORMAL_UP | float32[3] | 模型上方向向量 | 否,與NORMAL_RIGHT共存亡 |
NORMAL_RIGHT | float32[3] | 模型右方向向量,必須與up 向量正交 |
否,與NORMAL_UP共存亡 |
NORMAL_UP_OCT32P | uint16[2] | 模型上方向向量,32位精度八進制編碼 | 否,與NORMAL_RIGHT_OCT32P共存亡 |
NORMAL_RIGHT_OCT32P | uint16[2] | 模型右方向向量,必須與up 向量正交,32位精度8進制編碼 |
否,與NORMAL_UP_OCT32P共存亡 |
SCALE | float32 | 該 instance 對於 gltf 的縮放比例 | 否 |
SCALE_NON_UNIFORM | float32[3] | 該 instance 在三個方向上的縮放比例 | 否 |
BATCH_ID | uin8/uint16(默認)/uint32 | 用於在批量表裏檢索數據用的batchId | 否 |
當 i3dm 瓦片中逐個 instance 的POSITION
被定義時,量化座標 POSITION_QUANTIZED
就不該存在,反之亦然。
接下來四個方向向量屬性(法線)NORMAL_UP
、NORMAL_RIGHT
和 NORMAL_UP_OCT32P
、NORMAL_RIGHT_OCT32P
也是一對反依賴的逐要素屬性。
SCALE
屬性定義了當前要素(instance或實例)對使用的 gltf 模型的縮放比例。
SCALE_NON_UNIFORM
屬性與 SCALE
屬性差很少,只不過是在三個方向上分別不一樣的縮放比例。
BATCH_ID
,是當前要素(instance或實例)的 id 號,將 要素 與 批量表中的屬性 兩者聯繫起來。
我的以爲,應該叫
INSTANCE_ID
更合適一些?
若是不給定要素屬性中與方向有關的向量時,每一個實例的朝向有一個默認值:在WGS84橢球上,上方向指向正北,右方向指向正東。
上述全部屬性所有會記錄在要素表的 JSON 中,對於 全局屬性,其值記錄在 JSON 中,對於其要素屬性,由於要素(即instance)不少的時候寫在JSON中體積會變大,因此使用 JSON引用要素表二進制數據體 的形式。
下列是一個要素表的JSON:
{ INSTANCES_LENGTH : 4, // 有4個實例 POSITION : { byteOffset : 0 // POSITION的值從ftBinary的第0字節起開始計算 } }
讀者不妨回顧上一篇,b3dm的要素表JSON,並未出現有對要素表體引用的屬性,在這裏出現了:POSITION
,它從要素表體的第 0 個字節開始記錄數據。
而 POSITION
這個逐要素(實例、instance)屬性的定義,早已在上文說起,即三個 FLOAT
類型數字爲一組,一共 INSTANCES_LENGTH
組的數據,記錄在要素表體。這是 instance 座標數據,寫在 JSON 中雖然沒問題,可是會形成空間浪費,以二進制形式記錄會比較划算。
要素表JSON中引用的二進制數據均順次記錄在此。
批量表與 b3dm 的一致,均爲 JSON 記錄屬性元數據,批量表體記錄屬性具體數據。此處再也不舉例。
此部分參考官方文檔。
POSITION
的 i3dm 瓦片var featureTableJSON = { INSTANCES_LENGTH : 4, // 有4個實例 POSITION : { byteOffset : 0 // POSITION的值從ftBinary的第0字節起開始計算 } }; var featureTableBinary = new Buffer(new Float32Array([ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0 ]).buffer);
使用 JavaScript 語言記錄了 要素表JSON,以及要素表二進制數據(以ES6 TypedArray 形式)。
var featureTableJSON = { INSTANCES_LENGTH : 4, // 有4個實例 QUANTIZED_VOLUME_OFFSET : [-250.0, 0.0, -250.0], QUANTIZED_VOLUME_SCALE : [500.0, 0.0, 500.0], POSITION_QUANTIZED : { byteOffset : 0 }, NORMAL_UP_OCT32P : { byteOffset : 24 }, NORMAL_RIGHT_OCT32P : { byteOffset : 40 } }; var positionQuantizedBinary = new Buffer(new Uint16Array([ 0, 0, 0, 65535, 0, 0, 0, 0, 65535, 65535, 0, 65535 ]).buffer); var normalUpOct32PBinary = new Buffer(new Uint16Array([ 32768, 65535, 32768, 65535, 32768, 65535, 32768, 65535 ]).buffer); var normalRightOct32PBinary = new Buffer(new Uint16Array([ 65535, 32768, 65535, 32768, 65535, 32768, 65535, 32768 ]).buffer); var featureTableBinary = Buffer.concat([positionQuantizedBinary, normalUpOct32PBinary, normalRightOct32PBinary]);
規定了全局屬性 QUANTIZED_VOLUME_OFFSET
和 QUANTIZED_VOLUME_SCALE
,規定了量化座標 POSITION_QUANTIZED
、八進制上方向和右方向向量NORMAL_UP_OCT32P
、NORMAL_RIGHT_OCT32P
在要素表體中的起始偏移值。
因而,使用三個 TypedArray
構造的 Buffer
對象,再拼接在一塊兒,即要素表體 featureTableBinary
。
與b3dm裏寫的一致,能夠回看:http://www.javashuo.com/article/p-hcrvzhto-nh.html
一樣,這部份內容與b3dm篇章內介紹的一致,會在後續文章內介紹。