1. 程式人生 > >3dTiles 資料規範詳解[4.2] i3dm瓦片二進位制資料檔案結構

3dTiles 資料規範詳解[4.2] i3dm瓦片二進位制資料檔案結構

i3dm,即 `Instanced 3D Model`,例項三維模型的意思。 諸如樹木、路燈、路邊的垃圾桶、長椅等具有明顯 **重複** 特徵的資料。這類資料用得較少(笑,現在都喜歡搞BIM、傾斜攝影、精模、白模等) > 我的git地址:[github.com/onsummer](https://github.com/onsummer) > 轉載請規範化轉載。出處:@秋意正寒 https://www.cnblogs.com/onsummer/p/13252897.html # 瓦片檔案二進位制佈局(檔案結構) 與 b3dm 一致,檔案頭多了個屬性。 ![](https://img2020.cnblogs.com/blog/1097074/202007/1097074-20200714005624151-452521445.png) # 1. 檔案頭:32byte 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意義一樣,不做解釋。 第8個,`gltfFormat` 只有兩個值:0和1. 0,則位於 i3dm 瓦片檔案最後的 gltf 內容是一個 uri,指向gltf的資料內容(可能是Base64 DataURL,也可能是其他地方的地址,筆者沒見過...) 1,則位於 i3dm 瓦片檔案最後的 gltf 內容是 二進位制的 glb,大多數情況見的是這個。 預設情況,gltf 是 y 軸朝上,3dTiles 是z軸朝上,需要座標轉換。 # 2. 要素表 在上篇,有介紹到要素表存在 **全域性屬性** 和 **要素屬性**。在 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)`,如下圖所示: ![](https://img2020.cnblogs.com/blog/1097074/202007/1097074-20200714005648576-314489611.png) 這樣,被細分出來的每一個 “小矩形”,都有它自己在這個矩形空間的量化座標,因為 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 中,對於 全域性屬性,其值記錄在 JSON 中,對於其要素屬性,因為要素(即instance)很多的時候寫在JSON中體積會變大,所以使用 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中引用的二進位制資料均順次記錄在此。 # 3. 批量表 批量表與 b3dm 的一致,均為 JSON 記錄屬性元資料,批量表體記錄屬性具體資料。此處不再舉例。 # 4. 要素舉例說明 此部分參考官方文件。 ## ① 僅有 `POSITION` 的 i3dm 瓦片 ``` JS 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 形式)。 ## ② 使用量化位置與八進位制方向向量 ``` JS 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`。 # 5. 位元組對齊與編碼端序 與b3dm裡寫的一致,可以回看:[https://www.cnblogs.com/onsummer/p/13252896.html](https://www.cnblogs.com/onsummer/p/13252896.html) # 6. 擴充套件(extensions)和額外資訊(extras) 同樣,這部分內容與b3dm篇章內介紹的一致,會在後續文章內