3dTiles 數據規範詳解[3] 內嵌在瓦片文件中的兩大數據表

轉載請聲明出處:全網@秋意正寒html

零、本篇前言

說實話,我很糾結是先介紹瓦片的二進制數據文件結構,仍是先介紹這兩個重要的表。思前想後,我決定仍是先介紹這兩個數據表。算法

由於這兩個表不先給讀者灌輸,那麼介紹到瓦片的二進制數據文件結構時,就滿嘴「晦澀難懂」啦。json

數據與模型

上文介紹到,瓦片的三維模型其實是由gltf承擔起來的(做爲glb格式嵌入到瓦片二進制文件中),那麼,除了模型數據,確定模型本身自己也有屬性數據的。canvas

就好比,門有長寬高、密度、生產日期等信息,樓棟模型有建築面積、樓層數等信息。api

因此,「屬性數據」 和 「模型」 是如何產生聯繫的呢?數組

早在個人博客《聊聊GIS數據的四個分層與GIS服務》中有說起,只需把模型的幾何數據做爲一個屬性,寫入屬性數據中,即把屬性數據和幾何數據並列,就能夠了。緩存

可是,在3dTiles中,模型數據是以glb的形式嵌入在瓦片文件中的(點雲直接就寫xyz和顏色信息了),模糊了二維中「要素」的概念,並且gltf規範看起來並無所謂的「要素」的概念,僅僅是對GPU友好的vertex、normal、texture等信息。佈局

如何讓gltf模型的每個模型,甚至每個三角面,甚至每個頂點打上「我屬於哪一個模型」的印記呢?咱們本篇稍稍晚一些介紹。大數據

再回憶一個重要的 3dTiles 理念:ui

3dTiles 規範自己不包含模型數據的定義,它僅僅記錄模型變成瓦片後的空間組織關係、模型與其屬性數據之間的關係。

因此,3dTiles 規範在瓦片二進制數據文件中,使用了兩個重要的表來記錄這種 」模型與屬性「 的聯繫:

  • FeatureTable(要素表)
  • BatchTable(批量表)

瓦片二進制數據文件的大體字節佈局結構

上一篇簡單提過,瓦片引用的二進制文件有4種,即:b3dm、i3dm、pnts、cmpt。

除去cmpt這個複合類型不談,前三種的大體佈局見上圖。

每一種瓦片二進制數據文件都有一個記錄該瓦片的文件頭信息,文件頭包括若干個因瓦片不一樣而不太一致的數據信息,緊隨其後的是兩大數據表:FeatureTable(我翻譯其爲:要素表)、BatchTable(我翻譯其爲:批量表)。

這兩個表既然是二進制的數據,儘管它名字裏帶「表」,可是卻不是二維表格,它更多的是一些 鍵值 信息。

關於不一樣瓦片二進制文件的這兩個表的結構,在後續博文會詳細介紹。

1、記錄渲染相關的數據:FeatureTable,要素表

在 b3dm 瓦片中,要素表記錄這個批量模型瓦片中模型的個數,這個模型單體在人類邏輯上不可再分。

(在房屋級別來看,房子並非單體,構造它的門、門把、窗戶、屋頂、牆等纔是模型單體;可是在模型殼子的普通表面建模數據中,房子就是一個簡單的模型)

要素表還能夠記錄當前瓦片的中心座標,以便gltf使用相對座標,壓縮頂點座標數字的數據量。

官方給的定義是:

要素表記錄的是與渲染有關的數據。

直球!聽不懂!

我來「翻譯」一下好了:

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

渲染相關,即有多少個模型,座標是相對的話相對於哪一個中心,若是是點雲的話顏色信息是什麼以及座標如何等;

渲染所需,例如頂點信息、法線貼圖材質信息均有glb部分完成。

咱們以pnts(點雲瓦片)舉例,它的要素表容許有兩大類數據(看不懂不要緊,以後的博客還會繼續介紹四種瓦片文件的結構):

  • 點屬性:記錄每一個點雲點的信息
屬性名 數據類型 描述 是否必須
POSITION float32 * 3 直角座標的點 是,除非下面的屬性存在
POSITION_QUANTIZED uint16 * 3 量化的直角座標點 是,除非上面的屬性存在
RGBA uint8 * 4 四通道顏色
RGB uint8 * 3 RGB顏色 /
RGB565 uint16 有損壓縮顏色,紅5綠6藍5,即65536種顏色 /
NORMAL float32 *3 法線 /
NORMAL_OCT16P uint8 * 2 點的法線,10進制單位向量,有16bit精度 /
BATCH_ID uint8/uint16(默認)/uint32 從BatchTable種檢索元數據的id /
  • 全局屬性:記錄整個點雲瓦片的信息
屬性名 數據類型 描述 是否必須
POINTS_LENGTH uint32 瓦片中點的數量。全部的點屬性的長度必須與這個同樣。
RTC_CENTER float32 * 3 若是全部點是相對於某個點定位的,那麼這個屬性就是這個相對的點的座標。 /
QUANTIZED_VOLUME_OFFSET float32 * 3 量化偏移值(不知道是什麼) 與下面的屬性必須同時存在
QUANTIZED_VOLUME_SCALE float32 * 3 量化縮放比例(不知道是什麼) 與上面的屬性必須同時存在
CONSTANT_RGBA uint8 * 4 爲全部點定義同一個顏色 /
BATCH_LENGTH uint32 BATCH_ID的個數 與點屬性中的BATCH_ID必須同時存在

簡略一瞥,能夠看出點雲由於沒有使用gltf模型(也不必),把點雲要渲染到屏幕上所需的座標、法線、顏色等信息寫在了要素表中。

若是仍是不能理解何爲「渲染相關」,那麼請閱讀後續四種瓦片文件格式的詳細介紹,相信你會有所收穫——有多是我表達比較菜。

要說明一個「業界黑話」:

在一個瓦片中,一個三維要素(GIS中的一般叫法)= 一個模型(圖形學、工業建模叫法) = 一個BATCH(3dtiles叫法)

而後,我向讀者隆重介紹要素表的簡單結構,由於要素表、批量表都是以 二進制 形式存儲,因此瞭解每一種瓦片的二進制數據佈局十分重要。

要素表的結構:JSON描述信息+要素表數據體

要素表緊隨在若干個字節的文件頭後,它自己還能夠再分爲 二進制的JSON文本頭 + 二進制的數據體

以下圖所示:

有火燒眉毛的讀者但願更進一步瞭解要素表了,不要急,後續篇章必定展開,例如如何讀取要素表和其中的數據等。

接下來,是另外一個數據表:批量表。

2、記錄屬性數據:BatchTable,批量表

若是把批量表刪除,那麼3dTiles數據還能正常渲染。

是的,批量表就是所謂的模型屬性表,批量表中每一個屬性數組的個數,就等於模型的個數,由於有多少個模型就對應多少個屬性嘛!

(嘿嘿,其實也有例外的狀況,咱們到後續聊3dtiles數據規範的擴展能力時,再把這個坑填上,否則怎麼說3dtiles很靈 [keng] 活 [die] 呢)

批量表相對比較自由,只要能與模型對得上號,想寫啥就寫啥。

批量表中的屬性數據 ↔ 模型的關聯

假定讀者在閱讀此 3dtiles 博客以前,已經對 gltf 數據規範有必定的瞭解。

gltf 數據有三層邏輯:Node ← Mesh ← Primitive。

其中,Primitive 即 gltf 數據規範中最小的圖形單位,其頂點定義由其下的 attributes 對象下 POSITION 屬性來尋找訪問器(Accessor),從而獲取到數據。

{
    ...
    "meshes": [
       {
           "primitives": [
               {
                   "attributes": {
                       "POSITION": 0,
                       "TEXTURE_0": 2
                       ...
                   },
                   "indices": 1,
                   "mode": 4,
                   "material": 0
               }
           ]
       } 
    ],
    ...
}

獲取到 POSITIONindices 對應的訪問器、緩存視圖、緩存文件後,便可獲取 gltf 模型的全部頂點,即幾何模型,即三維要素的幾何數據。

如今問題來了,如何將這些頂點 「打」 上一個印記呢?就像檢疫的豬肉同樣,打個印記,說明豬是健康的。

Cesium團隊在設計 3dtiles 規範的時候充分利用了 gltf 規範的特色:開源。所以,每個 primitive 被在其 attributes 中添加了額外的訪問器:_BATCHID

"primitives": [
    {
        "attributes": {
            "POSITION": 0,
            "_BATCHID": 3,
            "TEXTURE_0": 2
            ...
        },
        "indices": 1,
        "mode": 4,
        "material": 0
    }
]

它與 POSITION 等沒什麼兩樣,一樣會佔用一部分數據。聰明的讀者應該能想到了,若是每個頂點都有一個所謂的 _BATCHID 對應,那麼我隨便給個點,我不就知道這個點的 _BATCHID,從而就知道這個點屬於哪個 BATCH 了嗎?

翻翻前面的 「黑話」,GIS的讀者更容易關聯起來:

一個 BATCH (即三維要素)用本身的 BATCHID 與幾何數據一一對應,屬性數據也與這個 BATCHID 一一對應,由傳遞關係,那麼三維的幾何數據 (gltf) 也就能和 屬性數據 (批量表) 一一對應了。

不過遺憾的是,並非全部的瓦片都有 gltf 模型,例如pnts瓦片。

因此這個 幾何 與 屬性 兩大數據如何關聯,在以後的博文再具體問題具體分析。

關於這個模型和屬性的關聯,在b3dm瓦片的博文中要重點介紹。

批量表的結構:JSON描述信息+批量表數據本體

與 要素表 很像,批量表也是由: 二進制的JSON文本頭 + 二進制的數據體 構成的。以下圖所示:

關於兩個表的更深層次的數據內容,例如如何承載模型與模型之間的邏輯關係,如何記錄使用 Google 壓縮算法的模型數據,那就是後續文章的內容了。

3、結語

本篇沒有特定的數據做爲說明,寫得不太好,由於是第一次嘗試用本身的語言表達這兩大數據表,謀篇佈局能力不太行,還請讀者包涵。

這兩個數據表真正的內容,還要等到接下來的四篇對瓦片二進制文件的重點介紹中,才能細細講完,本篇僅僅做爲接下來四篇博客的 「總結」,也能夠說是 「介紹」。

附:CesiumJS API 如何查詢瓦片的批量表

咱們一般在Cesium中使用 點擊 事件,來獲取一個 BATCH,即三維要素。在Cesium API中,這個被叫作:Cesium3DTileFeature。

那麼,這個 Cesium3DTileFeature 就能訪問到它本身的批量表中的屬性數據:

const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function(movement) {
    let feature = scene.pick(movement.endPosition);
    if (feature instanceof Cesium.Cesium3DTileFeature) {
        let propertyNames = feature.getPropertyNames();
        let length = propertyNames.length;
        for (var i = 0; i < length; ++i) {
            let propertyName = propertyNames[i];
            console.log(propertyName + ': ' + feature.getProperty(propertyName));
        }
    }
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

用到了 Cesium3DTileFeature.getPropertyNames() 方法獲取批量表中全部屬性名,用了 Cesium3DTileFeature.getProperty(string Name) 來獲取對應屬性名的屬性值。更多 API 見官方文檔。

相關文章
相關標籤/搜索