M3D - 一個想突破又還有很多包袱的新生三維資料格式雜談

秋意正寒發表於2021-04-13

都說 “一流企業定標準,二流企業賣品牌,三流企業賣產品,四流企業做專案”,在面向類 WebGL 技術的三維瀏覽器端渲染的大規模流式資料載入技術中,等到了 MapGIS 主導的 m3d 標準。

1 門派

這一份濃濃的武漢味似乎是想在繼 Cesium 3d-Tiles、Esri i3s、超圖 S3M 後再在三維市場分一杯羹。

在前言中,吳老爺子帶路,洋洋灑灑的一隊人。

2 幾個術語簡析

  • m3d:Model of 3D,在原文第 4 節有指出,但是翻譯過來不太對的樣子。
  • glbx:glTF 資料規範中,對於 glb 格式的一種變種檔案。原文指的是 “gltf資料的擴充套件格式”,我不清楚 m3d 標準對 gltf 擴充套件了什麼頂層屬性,或者是沒有好好研究 glTF 規範,因為 glTF 規範本身在幾乎所有的屬性中都有 extensionsextras 的預留屬性,用於擴充套件各種不同邏輯的業務功能或渲染特徵。

3 結構簡析

image

結構的設計與 Esri i3s 略相類似,使用 Node 充當空間剖分的術語。

  • M3DDataInfo.mcj:一種 JSON 文字檔案,字尾名是 mcj,且此檔案的檔名可任意設定,用於描述整份 m3d 資料
  • RootNodeInfo.jsonNodeInfo<i>.json:對 node 的描述檔案,檔名可以自定義
  • *.m3d:m3d 檔案,一種壓縮檔案,可選壓縮格式有:,字尾名是m3d,檔名可以自定義

每份 m3d 資料,其根目錄下有

  • 一個 mcj 檔案描述整份資料
  • 一個 Shared 目錄存放 Shared.m3d 檔案
  • 一個 根節點(RootNode)json 描述檔案
  • 一個 Data 目錄用於存放子一級的節點目錄
    • 每個節點目錄下仍能擁有更下一級的節點目錄,可遞迴
    • 每個節點目錄下有一個對當前 node 的 json 描述檔案
    • 每個節點目錄下可擁有 n 個 m3d 檔案,這些被稱作 “瓦片”,用於管理不同行業的資料(點雲.m3d、傾斜攝影模型.m3d、bim.m3d、地質體.m3d)
      • 每個 m3d 檔案下壓縮了三個目錄:GeometryTextureAttribute,分別存放 *.glb/glbx*.jpg/png/webp*.json/bin 檔案用於儲存幾何、紋理、屬性資料

3.1 空間剖分方式

規範中的術語是 “資料樹形結構”,個人認為使用 空間剖分方式 更好,直觀。

使用了三種常規三維資料結構,即空間八叉樹、四叉樹、KD樹。

3.2 空間範圍表示方式

  • BoundingBox
  • BoundingSphere

以上兩種常見空間範圍體二選一。

其中,BoundingBox 使用的定義是 6 元素的 AABB 包圍盒,即經緯高三軸的最值。

4 M3DDataInfo.mcj 類定義

描述整個 m3d 檔案的 json 檔案,它擁有如下頂層屬性:

  • asset: string,基本資訊,簡單的字串
  • version: string,這個令我十分無語,UML 圖中是 string,在文下是 float,我認為 string 更合適。
  • dataName: string,資料名
  • guid: string,資料唯一識別符號
  • compressType: string,壓縮型別,可選 "zip""7z""rar"
  • spatialReference: string,空間參考,可選 "WGS84""CGCS2000"
  • treeType: string,空間剖分結構,可選 "QuadTree""OCTree""K-DTree""RTree",這點我也有點無語,在前文中並未提及 R樹,雖然 R樹在空間搜尋中還是很常見的。
  • lodType: string,層次細節型別,可選 "ADD""REPLACE"
  • boundingVolume: BoundingVolume
  • position: Point
  • rootNode: Uri
  • fieldInfo: Array<Field>

其中,BoundingVolume 在上文已提及,連同 PointField,在此簡單使用 Rust 結構體定義:

struct BoundingVolume {
  boundingBox: BoundingBox,
  boundingSphere: BoundingSphere,
}

struct BoundingBox {
  left: f64,
  top: f64,
  right: f64,
  bottom: f64,
  minHeight: f64,
  maxHeight: f64,
}

struct BoundingSphere {
  center: Point,
  radius: f64,
}

struct Point {
  x: f64, 
  y: f64,
  z: f64,
}

struct Field {
  name: String,
  type: String,
  alias: String,
  size: i32,
}

其中,Field 的 type 欄位可選值為 "bool""int16""uint16""int32""uint32""int64""uint64""float""double""wchar""text""date""time""timestamp"

5 NodeInfo.json 類定義

每個節點(node)的描述檔案是一個 json 檔案,它的頂層屬性有:

  • name: string
  • lodLevel: string,用字串是一個留餘量的設計,因為不一定 level 必須是數字
  • boundingVolume: BoundingVolume
  • lodMode: string,可選值 "distance""pixel"
  • lodType: string,可選值 "ADD""REPLACE"
  • lodError: string,切換誤差值,單位根據 lodMode 決定
  • transform: float[16],轉換矩陣
  • parentNode: NodeInfo
  • childrenNode: Array<NodeInfo>
  • uri: string
  • shared: Uri
  • tileDataInfoIndex: int,M3D 瓦片索引
  • tileDataInfoList: Array<TileDataInfo>,瓦片資料列表

其中,TileDataInfo 的類定義用 rust 表示可為:

struct TileDataInfo {
  tileData: Uri,
  geometry: Geometry,
  attribute: Attribute,
  texture: Uri,
  dataType: string, // 可選值 "Vector"、"TiltPhotography"、"Model"、"BIM"、"PointCloud"、"PipeLine"、"GeoModel"、"GeoGrid"、"GeoDrill"、"GeoSection",分別表示:向量、傾斜、模型、BIM、點雲、管線、地質體、地質體網格、地址鑽孔、地質剖面
}

struct Geometry {
  blobType: string, // 可選值 "glb"、"glbx"
  url: string,
  geoCompressType: string, // 只能是 "draco"
  geometryType: string, // 可選值 "Point"、"Line"、"Polygon"、"Surface"、"Entity"
}

struct Attribute {
  attType: string, // 可選值 "json"、"bin"
  uri: string,
}

6 *.m3d 檔案

文中對 m3d 的介紹在 7.4 節,就不復述了,主要提一點:

  • glbx:基於 glb 資料擴充套件了單體化資訊及地質體資料資訊的幾何要素檔案,字尾名為.glbx,檔名可自定義。

我覺得這個格式的提出是多餘的,槽點最後再談。

7. 地質模型幾何結構定義

由於本部分(原文位於7.4.3節)在原文中較為詳細,只撿幾個點提一提。

7.1 鑽孔模型幾何結構定義

略。

7.2 地質剖面模型幾何結構定義

剖面使用的是一個點列表表示的簡單封閉三維線,這是有缺點的,因為不能表達多剖面的情況。

7.3 地質體模型幾何結構定義

地質體在此處使用了多個索引三角面來定義,其實可以完全使用 glTF 來定義,只不過這裡簡化成了索引+頂點的形式。

7.4 網格模型幾何結構

這部分的設計略顯冗雜,有重複設計的嫌疑,沒有優化。

8 屬性檔案的設計

屬性檔案的型別在 M3DDataInfo 物件中定義,見本文第 4 部分。

它使用 “屬性記錄(Record)” 來描述一個屬性,又分了下列兩個類定義:

struct Record {
  oid: long,
  rcdValues: Vec<RcdValue>,
}

struct RcdValue<T> {
  fldName: String,
  rcdValue: T
}

關於此部分的吐槽留到最後。

9 單體化的定義

m3d 標準使用一個叫 oidTable 的表,記錄每個 “物件” 的單體資訊。

具體做法是,對每個頂點進行設定值,例如房屋A設定每個頂點的值是1,那麼就記錄在 oidTable 中即可。

並由此擴充套件 glb 檔案,構成 glbx 檔案。

單開一小節吐槽

在 Cesium 提出的 glTF 擴充套件中,有一個更為接近此設計的擴充:glTF 中 Primitive 的定義增加一個 _BATCHID,使得頂點屬性新增一個,與 POSITIONCOLOR 等同級別,使用此 _BATCHID 來區分頂點歸屬於哪個 batch。

利用 glTF 強大的擴充套件能力完全沒必要 “擴充套件 glb 檔案成 glbx”。

另外,單體化也是一個偽命題。因為傾斜攝影模型僅僅是有外觀的三角網模型,不具備精細模型和 GIS 要素的獨立性,所以才提出 “單體化” 這種詞。

在 GIS 資料、BIM 資料中就很少聽說過這詞,因為這兩種資料本身就有著完備的物件獨立資訊。

如果模型建模時,有完整的地理要素的定義,其實就具備了單體化的資訊,使用 GIS 中的 “要素” 完全可以區分開不同的物體。

10 Shared 資料夾

關於此資料夾沒有過多的描述,只有不到 1/4 頁紙。

它似乎是設計來存放公共資料的,用來提供 instance 能力。

11 RESTful 介面設計

11.1 資料資訊獲取服務:M3DData

url模板:<base-url>/services/<service-name>/M3dServer

例:

http://igserver.mapgis.com/igs/rest/services/wuhan-3d/M3dServer

調取方法是 HTTP GET,返回型別是 application/json

11.2 公共資源獲取服務:M3DSharedResources

url模板:<m3d-data-url>/shared-resources

例:

http://igserver.mapgis.com/igs/rest/services/wuhan-3d/M3dServer/shared-resource

調取方法是 HTTP GET,返回型別是 application/octet-stream

11.3 根節點資訊獲取服務:M3DRootNodeInfo

11.4 其他服務

為什麼寫兩個就不寫了?

因為原文定義已經很詳細了,無需複述。

12 優點

  • 幾何部分使用 glTF 定義
  • 能使用 webp 作為紋理貼圖
  • 容納了各行各與的業務資料
  • 規範定義較為完備,文件廢話不多,但是不失詳盡性
  • 參考了i3s的結構定義,設計上把幾何、紋理、屬性資料解耦
  • 在幾何部分能重視 draco 壓縮演算法

13 缺點

① 對 glTF 規範認知不全

沒有充分閱讀 glTF 規範

  • glTF 本身就含有紋理貼圖、取樣器、材質的定義,此處再將紋理貼圖單獨定義,但未指出究竟是幾何部分的 glb/glbx 獨立儲存幾何 + 紋理單獨存放在紋理目錄,亦或者是混用,這一點十分疑惑。

沒有充分利用 glTF 強大的擴充套件能力

  • glTF 本身就帶了十幾種世界各大組織提供的擴充套件能力,包括但不限於紋理壓縮、幾何壓縮、材質擴充套件

如無必要,勿增實體。glbx 是一種多餘的設計。

② 文件定義缺陷

定義歧義

對於路徑的定義,在原文見過 Uriurlstring 三種寫法。

對於空間剖分的定義,在檔案組織上,使用 Node 資料夾來組織,但是其下的各部分資料又使用 Tile 來定義,在原文第 3.2 節還專門說明了瓦片資料,卻不見 Node 的詳細定義;

在圖1和圖2中,對 Node 和 m3d 檔案的組織十分困惑,Node<i> 目錄應該是一個 Node,下面能掛載 N 個 m3d 檔案;但是在原文 3.3 節中,每個瓦片下面掛 0 或 1 個 m3d 檔案,卻不見瓦片的定義,只見 tile data(瓦片資料) 的定義。

asset 屬性按理說可以囊括 nameversionguiddataName 等屬性的,卻將他們分開,不知道設計上是否經過足夠的考慮。

型別錯誤

在 M3DDataInfo 的類定義中,對於 version 欄位的型別,表格裡是 float,表格上面的 UML 類圖又是 string,這種低階錯誤應該在校對的時候修正。

UML 描述不夠完整

屬性的預設值、屬性的可選性未指出。

屬性拼寫不規範

例如 geoCompressType,此屬性是 Geometry 類下的,按理說就不需要 geo 字首,只需保留 compressType 即可。

還有 attType,該簡寫的地方簡寫,此處就不該簡寫,它是 Attribute 物件的一個屬性,所以它應該是 type

RcdValue 物件應該完整的命名為 RecordValue,且其 rcdValue 應簡單命名為 value,它的型別還出現了模糊不清的定義,因為沒有人知道 value 是什麼值型別。

駝峰命名法、下劃線命名法混用就不說了。

③ 標準裡太多 “專有” 詞彙

  • 單體化:這個就不適用於全體行業資料,在本文第 9 節已詳盡提及。
  • 其他見 本文 14.②

④ 對一些屬性未完全解釋

  • lodType:對 "ADD""REPLACE" 未完全解釋
  • lodError:未解釋其詳細的值含義

14 槽點

① ArcGIS Server REST介面風格味道太重

在原文的第 8 部分體現的十分明顯,RESTful 風格其實完全沒有必要設計如此冗雜的 URL 模板。

② 對資料的行業層級和結構層級混淆

資料的結構層級是與行業無關的,例如 “向量” 是一種概念,向量資料可以囊括二維圖形、三維圖形資料,完全可以描述幾何部分。

行業層級必須基於結構層級去定義,才有物質基礎,這一點馬克思老爺子早就總結好了。如果一個行業的資料對其底層的結構不清楚,例如單提 “BIM資料”,不提是什麼格式,不提它的結構,那也只能是談判桌上的詞,落實不下來,談不了什麼自主創新。

③ 使用商業壓縮格式 rar,使用過多的壓縮格式

若為開放標準,而使用半封閉的 rar 格式,則必須在服務端多準備一個 rar 解壓的程式。

zip、7z 作為儲存時能有效減少體積,但是文章中並未提及這兩種壓縮方式的壓縮等級、壓縮與解壓縮的效能開銷,如果因為這些延長了檔案 IO 的時間,那是得不償失的。

這一點是 i3s 的資源 gzip 法則佔優,因為瀏覽器天生支援 gzip,並且開銷不算很大。

④ 擴充套件性不佳

若將來要新增氣象資料或時態資料,沒有留有冗餘的設計。

15 個人總結

定義這種型別的資料規範,不僅要有足夠充足的行業一線實踐經歷,還要在 Web 視覺化領域有足夠的底層技術積累。

從技術和資料的視角做定義,才具備完整的可實施性和一般適配性。

M3D 的提出豐富了我國對三維資料規範的標準庫,但是現在缺實現,生態不足,推廣幾乎沒有,如何讓各行各業買賬,還需要走相當長的路,務必腳踏實地,方可行百里路。

相關文章