見出し画像

GLBのフォーマットに関するメモ #0

自分でGLBファイルを読み込んで表示したい!
私と同じ境遇の方がいるかもしれないので、サイトや本で読んだ情報を頑張ってシンプルにまとめてみました。
極力ないようにはしていますが、間違った情報があるかもしれないので、公式の仕様書(英語)と見比べたら一番よいと思います。


GLBとは

3DにおけるJPEGとも呼ばれるスタンダードな3Dモデルのフォーマット「glTF」を、頂点バッファーやテクスチャ用の画像といったバイナリデータも1つのファイル内に含められるようにしたものです。
VR向けアバターのVRMファイルの元にもなりました。
今時の3Dモデルの拡張子と言えばGLBなのかもしれません!

全体的なフォーマット

「g l T F」マジック (0x46546C67) 4バイト
「2 0 0 0」バージョン (0x00000002) 4バイト
JSONチャンクのバイト長 4バイト
「J S O N」チャンク (0x4E4F534A) 4バイト
(以下JSONチャンク)

{
    "asset": {},
    "scenes": [],
    "scene": 0,
    "nodes": [],
    "cameras": [],
    "meshes": [],
    "accessors": [],
    "bufferViews": [],
    "buffers": [],
    "materials": [],
    "textures": [],
    "samplers": [],
    "images": [],
    "skins": [],
    "animations": [],
    "extensionsUsed": [],
    "extensionsRequired": []
}

バイナリチャンクのバイト長 4バイト
「B I N \0」チャンク (0x004E4942) 4バイト
(以下バイナリチャンク)

  • 前半は1つのJSONデータ、後半は1つまでのバイナリデータとなっています。

  • JSONのルートオブジェクト直下には、GLBの主な項目が全て並列に記録されており、参照する項目が階層構造になっていたりなどはしません。各項目は配列の番号指定で参照し合います。

  • 4バイトの文字列やバージョンやバイトサイズなどは全てリトルエンディアンで記録します。

  • バイナリチャンクに浮動小数点データを含めるには、 IEEE-754 形式のリトルエンディアンのfloatを使用する必要があります。

  • バイナリチャンクの行列データの順番は横方向が先です。OpenGL系は縦方向が先ですが気をつけましょう。WebGLやOpenGLで読みやすい形のバイナリなのにそんなはずはありませんでした。Column-major orderなので行列を縦(列方向)に先に入れていきます。

  • 各チャンクは4バイトの境界にそろえて開始、終了する必要があります。終了に関しては必要に応じてパディング(文字埋め)が必要になるということです。

  • バイナリチャンクの行列データが、4バイト未満の要素で構成された2x2や3x3の場合も、4バイト境界に揃える為に、パディングが必要なようです。

  • assetオブジェクトには、ファイルを作成したツール名や、バージョン2.0であることを記述します。

  • sceneには、最初に表示するデフォルトのシーンの番号を記述します。

  • extensionsUsed配列には、使用する拡張の名前を列挙します。

  • extensionsRequired配列は、ここに列挙した拡張を、ファイルを開いたソフトが実装していなかった場合に、エラーを出して読み込みを失敗させます。

各項目の参照関係

図にした画像は他の場所にたくさんあったので、ここでは箇条書きで表現してみます。
色分けでハート付けてます。

  • ❤scene ->

    • 🧡node ->

      • 💛mesh ->

        • 💚accessor -> 💚bufferView -> 💚buffer

        • 🩵material -> 💙texture -> 💙sampler, 💙image

      • 💛camera

      • 💜skin ->

        • 🧡node

  • 🩷animation ->

    • 🧡node

わかりにくいので、各部分ごとに見ていきます。

ノード

  • ❤scene

    • 🧡node

      • 💛mesh

      • 🧡node

        • 💛mesh

    • 🧡node (joint)

    • 🧡node

      • 💛camera

❤scene : ルート。配置するノードを指定する。
🧡node : 基本の構造。メッシュやジョイントやカメラ用に姿勢を指定する。
💛mesh : ジオメトリー情報。頂点属性のアクセッサーやマテリアルへの参照を指定する。
💛camera : 視点。平行投影か透視投影かなどを指定する。

シーンには複数のノードを子にすることができ、
ノードにも複数のノードを子にできます。
ノードだけでは意味をなさないため、メッシュを参照したりたり、ジョイント(リグとも呼ばれる)として使ったり、カメラを参照したりします。

バッファー

  • ❤scene -> 🧡node -> 💛mesh ->

    • 💚accessor (POSITION) ->

      • 💚bufferView ->

        • 💚buffer

    • 💚accessor (NORMAL) ->

      • 💚bufferView ->

        • 💚buffer

    • 💚accessor (TEXCOORD_0) ->

      • 💚bufferView ->

        • 💚buffer

    • 💚accessor (index) ->

      • 💚bufferView ->

        • 💚buffer

💚accessor : バッファーから読む値の型、個数、スカラーかベクトルか行列かを指定する。
💚bufferView : バッファーから読む値のオフセット、バイト長、記録されている間隔を指定する。
💚buffer : バッファーのサイズを指定する。

メッシュはアクセッサーを参照します。
アクセッサーは頂点座標、ノーマル、テクスチャ座標、頂点インデックスなど用途別に参照できます。

マテリアル

  • ❤scene -> 🧡node -> 💛mesh ->

    • 🩵material ->

      • 💙texture (pbrMetallicRoughness) ->

        • 💙sampler

        • 💙image

      • 💙texture (normal) ->

        • 💙sampler

        • 💙image

      • 💙texture (occlusion) ->

        • 💙sampler

        • 💙image

🩵material : レンダリング方法。カラー、ラフネス、メタリックなどのテクスチャの参照を指定する。
💙texture : サンプラーとイメージの組み合わせを指定する。
💙sampler : 画像の補間方法やリピート方法を指定する。
💙image : 画像を読み取るバッファービューを指定する。

メッシュからマテリアルを参照し、マテリアルはメタリック・ラフネステクスチャ、ノーマルテクスチャ、アンビエント・オクルージョンテクスチャなどを参照しています。

スキン

  • ❤scene -> 🧡node ->

    • 💜skin ->

      • 🧡node (joint)

    • 💛mesh ->

      • 💚accessor (JOINTS_0) ->

        • 💚bufferView ->

          • 💚buffer

      • 💚accessor (WEIGHTS_0) ->

        • 💚bufferView ->

          • 💚buffer

💜skin : ジョイントとして使うノードはどれらか、ルートのジョイントはどれかなどを指定する。

ノードはジョイントによって影響を受ける場合はスキンを参照し、
スキンもジョイントとして扱うノードを参照します。
メッシュは影響を受けるジョイント番号や、その影響の重みを読み取るアクセッサーを指定でき、メッシュそのものはジョイントによって変形します。

アニメーション

  • 🩷animation ->

    • 🧡node

    • 💚accessor (input) ->

      • 💚bufferView->

        • 💚buffer

    • 💚accessor (output) ->

      • 💚bufferView ->

        • 💚buffer

🩷animation : ノードの姿勢を滑らかに補間してアニメーションさせる。キーフレー厶とターゲットのアクセッサーなどを指定する。

アニメーションはノードと、キーフレームを読み取るインプットのアクセッサー、アニメーション先のターゲットの値を読み取るアウトプットのアクセッサーを参照します。
インプットとアウトプットの組み合わせである「アニメーションサンプラー」は、上述したテクスチャのサンプラーとは全くの無関係です。

あとがき

少しでもみなさんの役に立てれば幸いです。
いいねやコメントお待ちしております!

参考にしたサイトや本

gltfOverviewJapanese

glTF 覚え書き

3DモデルフォーマットglTFに詳しくなる本

この記事が気に入ったらサポートをしてみませんか?