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

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

> 原創。轉載請規範註明出處:https://www.cnblogs.com/onsummer/p/13252896.html > 我的git地址:[github.com/onsummer](https://github.com/onsummer) B3dm,Batched 3D Model,成批量的三維模型的意思。 傾斜攝影資料(例如osgb)、BIM資料(如rvt)、傳統三維模型(如obj、dae、3dMax製作的模型等),均可建立此類瓦片。 # 瓦片檔案二進位制佈局(檔案結構) ![](https://img2020.cnblogs.com/blog/1097074/202007/1097074-20200713010403920-1500247603.png) ## ① 檔案頭:佔28位元組(byte) 位於b3dm檔案最開頭的28個位元組,是7個屬性資料: | 屬性的官方名稱 | 位元組長 | 型別 | 含義 | | ------------------------------ | ------ | ------------------- | ------------------------------------- | | `magic` | 4 | string(或char[4]) | 該瓦片檔案的型別,在b3dm中是 `"b3dm"` | | `version` | 4 | uint32 | 該瓦片的版本,目前限定是 1. | | `byteLength` | 4 | uint32 | 該瓦片檔案的檔案大小,單位:byte | | `featureTableJSONByteLength` | 4 | uint32 | 要素表的JSON文字(二進位制形式)長度 | | `featureTableBinaryByteLength` | 4 | uint32 | 要素表的二進位制資料長度 | | `batchTableJSONByteLength` | 4 | uint32 | 批量表的JSON文字(二進位制形式)長度 | | `batchTableBinaryByteLength` | 4 | uint32 | 批量表的二進位制資料長度 | 其中, `byteLength` = 28 + `featureTableJSONByteLength` + `featureTableBinaryByteLength` + `batchTableJSONByteLength` + `batchTableBinaryByteLength` ## ② 要素表 回顧上篇,我說的是 > 要素表,記錄的是整個瓦片渲染相關的資料,而不是渲染所需的資料。 那麼,b3dm瓦片中的要素表會記錄哪些資料呢? ### 全域性屬性 什麼是全域性屬性?即對於瓦片每一個三維模型(或BATCH、要素)或者直接對當前瓦片有效的資料,在b3dm中,要素表有以下全域性屬性: | 屬性名 | 屬性資料型別 | 屬性描述 | 是否必須存在 | | -------------- | ------------ | ------------------------------------------------ | ------------ | | `BATCH_LENGTH` | uint32 | 當前瓦片檔案內三維模型(BATCH、要素)的個數 | yes | | `RTC_CENTER` | float32[3] | 如果模型的座標是相對座標,那麼相對座標的中心即此 | no | 注意,如果glb模型並不需要屬性資料,即要素表和批量表有可能是空表,那麼 `BATCH_LENGTH` 的值應設為 0 . ### *要素屬性 對於每個模型(BATCH、要素)各自獨立的資料。在b3dm中沒有。 我們回憶一下要素表的定義:與渲染相關的資料。 b3dm瓦片與渲染相關的資料都在glb中了,所以b3dm並不需要儲存每個模型各自獨立的資料,即不存在要素屬性。 > *在i3dm、pnts兩種瓦片中,要素屬性會非常多。* ### 全域性屬性存在哪裡? 全域性屬性儲存在 要素表的JSON中,見下文: ### JSON頭部資料 由上圖可知,檔案頭28位元組資料之後是要素表,要素表前部是 長達 `featureTableJSONByteLength` 位元組的二進位制JSON文字,利用各種語言內建的API可以將這段二進位制資料轉換為字串,然後解析為JSON物件。 例如,這裡解析了一個b3dm檔案的 要素表JSON: ``` JSON { "BATCH_LENGTH": 4 } ``` 那麼,此b3dm瓦片就有4個模型(4個要素,或4個BATCH),其 `batchId` 是0、1、2、3. ### 要素表的二進位制本體資料 無。 > *注:* > > *當要素表的 JSON 資料以引用二進位制體的方式出現時,資料才會記錄在要素表的二進位制本體資料中,此時JSON記錄的是位元組偏移量等資訊。* > > *但是在b3dm瓦片中,要素表只需要JSON就可以了,不需要自找麻煩再引用二進位制資料,因為`BATCH_LENGTH` 和 `RTC_CENTER` 都相對好記錄,一個是數值,一個是3元素的陣列。* > > *在下面的要介紹批量表中,就會出現 JSON 資料引用二進位制體的情況了。在 i3dm 和 pnts 瓦片中,要素表 JSON就會大量引用其二進位制體。* ## ③ 批量表 批量表記錄的是每個模型的屬性資料,以及擴充套件資料(擴充套件資料以後再談)。 要素表和批量表唯一的聯絡就是 `BATCH_LENGTH`,在 i3dm 中叫 `INSTANCE_LENGTH`,在 pnts 中叫 `POINTS_LENGTH`。 這很好理解,要素表記錄了有多少個模型(BATCH、要素),那麼批量表每個屬性就有多少個值。 ### JSON頭部資料 先上一份批量表的JSON看看: ``` JSON { "height" : { "byteOffset" : 0, "componentType" : "FLOAT", "type" : "SCALAR" }, "geographic" : { "byteOffset" : 40, "componentType" : "DOUBLE", "type" : "VEC3" }, } ``` 這個批量表的JSON有兩個屬性:`height`、`geographic`,字面義即模型的高度值、地理座標值。 `height` 屬性通過其 `componentType` 指定資料的值型別為 `FLOAT`,通過其 `type` 指定資料的元素型別為 `SCALAR`(即標量)。 `geographic` 屬性通過其 `componentType` 指定資料的的值型別是 `DOUBLE`,通過其 `type` 指定資料的元素型別為 `VEC3`(即3個double數字構成的三維向量)。 `byteOffset` ,即這個屬性值在 **二進位制本體資料** 中從哪個位元組開始儲存。 從上表可以看出,height 屬性跨越 0 ~ 39 位元組,一共40個位元組。 **通過 FeatureTableJSON 可以獲取 BATCH_LENGTH 為10,那麼就有10個模型,對應的,這 40 個位元組就儲存了10個 height 值,查相關資料得知,FLOAT型別的資料位元組長度為 4,剛好 4 byte × 10 = 40 byte,即 height 屬性的全部資料的總長。** geographic 屬性也同理,VEC3 代表一個 geographic 有3個 DOUBLE 型別的數字,一個 DOUBLE 數值佔 8byte,那麼 geographic 一共資料總長是: type × componentType × BATCH_LENGTH = 3 × 8byte × 10 = 240 byte. ![](https://img2020.cnblogs.com/blog/1097074/202007/1097074-20200713004533574-845350488.png) **事實上,兩個屬性的總長是 40 + 240 = 280 byte,與 b3dm 檔案頭中的第七個屬性 `batchTableBinaryByteLength` = 280 是一致的。** ### 二進位制本體資料 二進位制本體資料即批量表中每個屬性的順次儲存。 ### 能不能不寫二進位制本體資料? 可以。如果你覺得資料量比較小,可以直接把資料寫在 `BatchTableJSON` 中,還是以上述兩個資料為例: ``` json { "height": [10.0, 20.0, 15.0, ...], // 太長了不寫那麼多,一共10個 "geographic": [ [113.42, 23.10, 102.1], [111.08, 22.98, 24.94], // 太長不寫 ] } ``` 但是,讀者請一定注意這一點:同樣是一個數字,二進位制的JSON文字大多數時候體積會比二進位制資料大。因為JSON文字還包括括號、逗號、冒號等JSON文字必須的符號。對於屬性資料相當大的情況,建議使用 JSON引用二進位制本體資料的組織形式,此時JSON充當的角色是元資料。 **注意:**對於屬性的值型別是 JSON 中的 object、string、bool 型別,則必須存於 JSON 中,因為二進位制體只能存 標量、234維向量四種類型的數字資料。 ## ④ 內嵌的glb 本部分略,對glb資料感興趣的讀者可自行查閱 glTF 資料規範。 關於兩大資料表如何與glb每一個頂點進行關聯的,在前篇也有簡略介紹。可以參考官方的說明: https://github.com/CesiumGS/3d-tiles/tree/master/specification/TileFormats/Batched3DModel#binary-gltf ## ⑤ 位元組對齊與編碼端序 ### JSON二進位制文字對齊 FeatureTableJSON、BatchTableJSON的二進位制文字,最後一個位元組相對於整個b3dm檔案來說,偏移量必須是8的倍數。 如果不對齊,必須用二進位制空格(即 `0x20`)填夠。 你問我為啥不對起始偏移量也要求 8byte 對齊?因為 FeatureTableJSON 之前是28byte的 檔案頭,為了湊齊8倍數對齊,檔案頭和 FeatureTableJSON 還要塞4個位元組填滿,那就有點多餘了。 末尾對齊,即 (28 + ftJSON長)能整除8,(28 + ftTable長 + btJSON長)能整除8. ### 資料體的起始、末尾對齊 二進位制資料體,無論是要素表、批量表,首個位元組相對於b3dm檔案的位元組偏移量,必須是8的倍數,結束位元組的位元組偏移量,也必須是8的倍數。 如果不滿足,可以填充任意資料位元組滿足此要求。 特別的,二進位制資料體中,每一個屬性值的第一個數值的第一個位元組的偏移量,相對於整個b3dm檔案,必須是其 `componentType` 的倍數,如果不滿足,則必須用空白位元組填滿。 例如,上述 height 屬性所在的批量表二進位制資料體,理所當然位於批量表JSON之後,而批量表的JSON又是8byte對齊的,假設批量表的資料體起始位元組是800,那麼 height 的第一個值起始位元組就是 800,由於 height 屬性的 componentType 是 FLOAT,即 4位元組,800 ÷ 4 能整除,所以沒有問題。 但是,假如 換一個屬性,其 componentType 是 `BYTE`,即 1位元組,那麼假設第二個屬性的 componentType 是 DOUBLE,即 8位元組,就會出現 第二個屬性的第一個值起始偏移量是810,810 ÷ 8 並不能整除,必須補齊 6個空白位元組,以滿足第二個屬性第一個值的起始偏移量是 810+6 = 816位元組。 ### 編碼端序 要素表、批量表的二進位制資料,無論是JSON還是資料體,均使用小端序編碼(LittleEndian)。 ## ⑥ 擴充套件資料(extensions)與額外補充資訊(extras) 其實,無論是要素表,還是批量表,都允許在JSON中存在擴充套件資料,以擴充當前瓦片模型的功能,而並不是單一的一個一個模型順次儲存在瓦片檔案、glb中。 有關擴充套件資料,在以後會專門出一篇部落格