3dTiles 數據規范詳解[4.1] b3dm瓦片二進制數據文件結構


原創。轉載請規范注明出處:https://www.cnblogs.com/onsummer/p/13252896.html
我的git地址:github.com/onsummer
目錄:https://www.cnblogs.com/onsummer/p/12799366.html

B3dm,Batched 3D Model,成批量的三維模型的意思。

傾斜攝影數據(例如osgb)、BIM數據(如rvt)、傳統三維模型(如obj、dae、3dMax制作的模型等),均可創建此類瓦片。

瓦片文件二進制布局(文件結構)

① 文件頭:占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 + glb的字節長度

② 要素表

回顧上篇,我說的是

要素表,記錄的是整個瓦片渲染相關的數據,而不是渲染所需的數據。

那么,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:

{
    "BATCH_LENGTH": 4
}

那么,此b3dm瓦片就有4個模型(4個要素,或4個BATCH),其 batchId 是0、1、2、3.

要素表的二進制本體數據

無。

注:

當要素表的 JSON 數據以引用二進制體的方式出現時,數據才會記錄在要素表的二進制本體數據中,此時JSON記錄的是字節偏移量等信息。

但是在b3dm瓦片中,要素表只需要JSON就可以了,不需要自找麻煩再引用二進制數據,因為BATCH_LENGTHRTC_CENTER 都相對好記錄,一個是數值,一個是3元素的數組。

在下面的要介紹批量表中,就會出現 JSON 數據引用二進制體的情況了。在 i3dm 和 pnts 瓦片中,要素表 JSON就會大量引用其二進制體。

③ 批量表

批量表記錄的是每個模型的屬性數據,以及擴展數據(擴展數據以后再談)。

要素表和批量表唯一的聯系就是 BATCH_LENGTH,在 i3dm 中叫 INSTANCE_LENGTH,在 pnts 中叫 POINTS_LENGTH

這很好理解,要素表記錄了有多少個模型(BATCH、要素),那么批量表每個屬性就有多少個值。

JSON頭部數據

先上一份批量表的JSON看看:

{
    "height" : {
        "byteOffset" : 0,
        "componentType" : "FLOAT",
        "type" : "SCALAR"
    },   
    "geographic" : {
        "byteOffset" : 40,
        "componentType" : "DOUBLE",
        "type" : "VEC3"
    },
}

這個批量表的JSON有兩個屬性:heightgeographic,字面義即模型的高度值、地理坐標值。

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.

事實上,兩個屬性的總長是 40 + 240 = 280 byte,與 b3dm 文件頭中的第七個屬性 batchTableBinaryByteLength = 280 是一致的。

二進制本體數據

二進制本體數據即批量表中每個屬性的順次存儲。

能不能不寫二進制本體數據?

可以。如果你覺得數據量比較小,可以直接把數據寫在 BatchTableJSON 中,還是以上述兩個數據為例:

{
    "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中。

有關擴展數據,在以后會專門出一篇博客介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM