3D画像ファイルの画像とは?
glTFファイルフォーマットを解きほぐす
はじめに
3次元(以下3D)画像ファイルはこれまで様々な用途で便利に使われており、ますます進化しているようです。
いろいろな種類の3D画像ファイルについての紹介記事や、それらのファイルフォーマットの解説記事は数多くあって、3D画像ファイルを扱う人たちにとって大いに役立つ情報になっています。
ここでは、そういう3D画像ファイルは画像データをどのように扱って記録しているのか?に焦点を当てて、分かり易く解きほぐしたいと考えています。
前回のでは、3D画像ファイルで「基本的」とされているSTLファイルフォーマットについて調べました。
今回は、
引用:次世代の3Dデータフォーマット決定版 glTF 2.0
などの記事があるので、
3D画像ファイルで「決定版」とされている を解きほぐしてみることにしました。
glTF 2.0ファイルフォーマットの仕様
glTF 2.0ファイルフォーマットの仕様については、The glTF 2.0 SpecificationやglTFのご紹介などに書かれています。2.0は最新バージョン番号です。他にも沢山の紹介記事が見つかります。
このフォーマットは、3次元コンピュータグラフィックス(以下3DCG)の殆ど全てのデータを格納できる構造になっていて、このため「決定版」と呼ばれていると思われます。
前回取り上げた「基本的」とされているSTLファイルと比較してみると、glTFファイルの特徴的な構造が理解し易くなると思います。
STLファイルは、頂点座標値、法線ベクトルなどからなるポリゴンデータを格納して3D物体の表面形状(ジオメトリ)を表すファイルでした。そのポリゴンデータは3DCG技術における基本的なデータです。3DCGのデータには他にもいろいろなデータがあります。glTFファイルにはそういうSTLファイルにはないデータも格納されるようになっています。
それらのglTFファイルの特徴的なデータを説明していきます。
glTF 2.0ファイルフォーマットには、
などの特徴的なファイル構成、データの格納方法についての仕様もありますが、これら構造についてはここでは触れません。上記glTFファイルフォーマットの記事を参照してください。
頂点インデックス
最初は頂点インデックスです。
glTFファイルの広範なサポート機能を象徴的に示す簡単な例ではないかと思って最初に挙げました。
具体例1
今回も著名なオープンソースの3DCGソフトであるBlenderから出力された、単純な立方体モデルのglbファイル(バイナリ)を拝借してその構造を説明します。
法線ベクトルなしのモードにすると(Blenderでメッシュモードなし)、glbファイルのバイナリデータには、8個の頂点の座標値と36個のインデックス(indices)データだけが格納されます。
glbファイルのJSONデータには、ジオメトリを示す"meshes"情報の"primitives"は、
"primitives"
{
"attributes":
{
"POSITION":0
},
"indices":1,
"material":0
}
となっています。これにより、頂点の座標値とインデックス(indices)データが格納されることが示され、所定の場所からそれらデータが取り出されます。
頂点座標値
各頂点に対し、
バイト数 データ型
12Byte float × 3
計8個×12=96Byteのデータが格納されています。
この8個の座標値(POSITION値)は立方体の8個の頂点に対応しており【図1】に右手座標系として割り当てて表示しています。
頂点0、1、2、・・・の番号はそれら頂点座標値データの格納順です。
記号Pは筆者が適当に付与した記号です。
具体例1のデータ
POSITION
頂点0の座標値 P0 (1,1,-1)
頂点1の座標値 P1 (1,-1,-1)
頂点2の座標値 P2 (1,1,1)
頂点3の座標値 P3 (1,-1,1)
頂点4の座標値 P4 (-1,1,-1)
頂点5の座標値 P5 (-1,-1,-1)
頂点6の座標値 P6 (-1,1,1)
頂点7の座標値 P7 (-1,-1,1)
インデックスデータ
バイト数 データ型
2Byte unsigned short
計36個×2=72Byteのデータが格納されています。
インデックスデータは計36個のデータで、3個毎に空白があるのは、3個毎にポリゴンを表しているので分かり易いように付与しています。
実際のデータは36個が羅列されています。
この36個の数字は頂点の番号に対応しています。
その3個の数字の頂点を結ぶと、【図2】に示すように計12個の三角形ポリゴンになります。
具体例1のデータ
頂点インデックスの解説
頂点座標は12Byteで表されるので、36×12=432Byteの容量になります。
これに対し上記の具体例1では、8個の頂点座標値は計96Byteで、インデックスデータは72Byteですので計168Byteの容量となり、STLファイルに比べて小さくなっていることが分かります。
このように簡素化できることが頂点インデックスの利点になります。
また、3個の頂点インデックスの順番を反時計回りなどに限定すれば法線ベクトルを計算できるので、法線ベクトルを格納する容量も削減できることになります。
今回の簡単な具体例1でも法線ベクトルは記録されていません。
頂点インデックスがあって法線ベクトルがない簡素な構造でもglTFファイルとして扱えるということです。
法線ベクトル
次は法線ベクトルです。
glTFファイルにおける法線ベクトルの扱い方、頂点法線という不思議な概念との関係を考えます。
具体例2
同じく、Blenderから出力された、単純な立方体モデルのglbファイル(バイナリ)を拝借してその構造を説明します。
法線ベクトルありのモードにすると(Blenderでメッシュモードのノーマルあり)、glbファイルのバイナリデータには、24個の頂点の座標値と24個の法線ベクトルと36個のインデックス(indices)データが格納されます。
glbファイルのJSONデータには、ジオメトリを示す"meshes"情報の"primitives"は、
"primitives"
{
"attributes":
{
"POSITION":0
"NORMAL":1
},
"indices":2,
"material":0
}
となっています。これにより、頂点の座標値と法線ベクトルとインデックス(indices)データが格納されることが示され、所定の場所からそれらデータが取り出されます。
頂点座標値
各頂点に対し、
バイト数 データ型
12Byte float × 3
計24個×12=288Byteのデータが格納されています。
この24個の座標値(POSITION値)は、立方体の8個の頂点が属している面を区別するために、異なる3個の面のそれぞれに異なる頂点番号が割り振られています。
そのため、各頂点に3個の座標値が重複して記録されています。
【図3】に右手座標系として割り当てて表示しています。
頂点0、1、2、・・・の番号は具体例1と同様にそれら頂点座標値データの格納順です。
記号Pは筆者が適当に付与した記号です。
具体例2のデータ
POSITION
頂点0の座標値 P0 (1,1,-1)
頂点1の座標値 P1 (1,1,-1)
頂点2の座標値 P2 (1,1,-1)
頂点3の座標値 P3 (1,-1,-1)
頂点4の座標値 P4 (1,-1,-1)
頂点5の座標値 P5 (1,-1,-1)
頂点6の座標値 P6 (1,1,1)
頂点7の座標値 P7 (1,1,1)
頂点8の座標値 P8 (1,1,1)
頂点9の座標値 P9 (1,-1,1)
頂点10の座標値 P10 (1,-1,1)
頂点11の座標値 P11 (1,-1,1)
頂点12の座標値 P12 (-1,1,-1)
頂点13の座標値 P13 (-1,1,-1)
頂点14の座標値 P14 (-1,1,-1)
頂点15の座標値 P15 (-1,-1,-1)
頂点16の座標値 P16 (-1,-1,-1)
頂点17の座標値 P17 (-1,-1,-1)
頂点18の座標値 P18 (-1,1,1)
頂点19の座標値 P19 (-1,1,1)
頂点20の座標値 P20 (-1,1,1)
頂点21の座標値 P21 (-1,-1,1)
頂点22の座標値 P22 (-1,-1,1)
頂点23の座標値 P23 (-1,-1,1)
法線ベクトル
各頂点に対し、
バイト数 データ型
12Byte float × 3
計24個×12=288Byteのデータが格納されています。
この24個の法線ベクトル値(NORMAL値)は、上記24個の頂点が属している面の法線ベクトルを示しています。
立方体は6個の面があるので異なる6個の法線ベクトルがあり、各面には4個の頂点があるから、4個の法線ベクトルが重複して記録されています。
法線ベクトル0、1、2、・・・の番号はそれら法線ベクトルデータの格納順です。
頂点座標値の番号に対応しています。
記号Nは筆者が適当に付与した記号です。
【図4】に立方体の各面を外側に移動させてそれぞれの面に属する頂点番号を記載し、各頂点番号と面との関係を表示しています。
具体例2のデータ
NORMAL
頂点0の法線ベクトル N0 (0,0,-1)
頂点1の法線ベクトル N1 (0,1,-0)
頂点2の法線ベクトル N2 (1,0,-0)
頂点3の法線ベクトル N3 (0,-1,-0)
頂点4の法線ベクトル N4 (0,0,-1)
頂点5の法線ベクトル N5 (1,0,-0)
頂点6の法線ベクトル N6 (0,0,1)
頂点7の法線ベクトル N7 (0,1,-0)
頂点8の法線ベクトル N8 (1,0,-0)
頂点9の法線ベクトル N9 (0,-1,-0)
頂点10の法線ベクトル N10(0,0,1)
頂点11の法線ベクトル N11(1,0,-0)
頂点12の法線ベクトル N12(-1,0,-0)
頂点13の法線ベクトル N13(0,0,-1)
頂点14の法線ベクトル N14(0,1,-0)
頂点15の法線ベクトル N15(-1,0,-0)
頂点16の法線ベクトル N16(0,-1,-0)
頂点17の法線ベクトル N17(0,0,-1)
頂点18の法線ベクトル N18(-1,0,-0)
頂点19の法線ベクトル N19(0,0,1)
頂点20の法線ベクトル N20(0,1,-0)
頂点21の法線ベクトル N21(-1,0,-0)
頂点22の法線ベクトル N22(0,-1,-0)
頂点23の法線ベクトル N23(0,0,1)
インデックスデータ
バイト数 データ型
2Byte unsigned short
計36個×2=72Byteのデータが格納されています。
インデックスデータは計36個のデータで、3個毎に空白があるのは、3個毎にポリゴンを表しているので分かり易いように付与しています。
実際のデータは36個が羅列されています。
この36個の数字は頂点の番号に対応しています。
その3個の数字の頂点を結ぶと、【図4】に示すように計12個の三角形ポリゴンになります。
具体例1と異なるのは、同じ頂点であっても頂点番号が面毎に異なる点ですが、12個の三角形ポリゴンは変わりません。
具体例2のデータ
法線ベクトルの解説
点の法線ベクトルと考えると意味不明になりますが、頂点が属する面の法線ベクトルであると考えると理解できます。
その属する面の向きで処理を変える場合などで使われるのでしょう。
頂点カラー
次は頂点カラーです。
ここまで述べてきた頂点インデックス、法線ベクトルはどれもジオメトリ構造の仕組みであって画像情報は扱っていませんでした。頂点カラーで初めて画像情報に触れることになります。
具体例3
同じく、Blenderから出力された、単純な立方体モデルのglbファイル(バイナリ)を拝借してその構造を説明します。
頂点カラーありのモードにすると(Blenderでメッシュモードの頂点カラー+ノーマルあり、BaseColorを(1,1,1)にして、頂点ペイントで赤色を図3のP0、P1、P2である1個の頂点だけに塗ってテスト)、glbファイルのバイナリデータには、24個の頂点の座標値と24個の法線ベクトルと36個のインデックス(indices)データと24個の頂点カラーデータが格納されます。
glbファイルのJSONデータには、ジオメトリを示す"meshes"情報の"primitives"は、
"primitives"
{
"attributes":
{
"COLOR_0":0,
"POSITION":1
"NORMAL":2
},
"indices":3,
"material":0
}
となっています。これにより、頂点の座標値と法線ベクトルとインデックス(indices)データと頂点カラーデータが格納されることが示され、所定の場所からそれらデータが取り出されます。
頂点の座標値と法線ベクトルは具体例2と同じですので、頂点カラーデータのみ以下にデータを示します。
頂点カラー値
各頂点に対し、
バイト数 データ型
8Byte unsigned short × 4
計24個×8=192Byteのデータが格納されています。
この24個の頂点カラー値(COLOR_0値)は、上記具体例2の24個の頂点に付加されています。
頂点カラー値0、1、2、・・・の番号は具体例2と同様にそれら頂点カラー値データの格納順で、頂点座標値の番号と対応しています。
記号Cは筆者が適当に付与した記号です。
頂点カラー値は赤(R)、緑(G)、青(B)の三原色強度と、透明度(A)データからなり、それぞれ0~1の値で2Byteであるので16進数ではffffが1で0000が0を表します。
透明度はここでは1が100%不透明になります。
具体例3のデータ
COLOR_0
頂点0の頂点カラー値 C0 (1,0,0,1)
頂点1の頂点カラー値 C1 (1,0,0,1)
頂点2の頂点カラー値 C2 (1,0,0,1)
頂点3の頂点カラー値 C3 (1,1,1,1)
頂点4の頂点カラー値 C4 (1,1,1,1)
頂点5の頂点カラー値 C5 (1,1,1,1)
頂点6の頂点カラー値 C6 (1,1,1,1)
頂点7の頂点カラー値 C7 (1,1,1,1)
頂点8の頂点カラー値 C8 (1,1,1,1)
頂点9の頂点カラー値 C9 (1,1,1,1)
頂点10の頂点カラー値 C10 (1,1,1,1)
頂点11の頂点カラー値 C11 (1,1,1,1)
頂点12の頂点カラー値 C12 (1,1,1,1)
頂点13の頂点カラー値 C13 (1,1,1,1)
頂点14の頂点カラー値 C14 (1,1,1,1)
頂点15の頂点カラー値 C15 (1,1,1,1)
頂点16の頂点カラー値 C16 (1,1,1,1)
頂点17の頂点カラー値 C17 (1,1,1,1)
頂点18の頂点カラー値 C18 (1,1,1,1)
頂点19の頂点カラー値 C19 (1,1,1,1)
頂点20の頂点カラー値 C20 (1,1,1,1)
頂点21の頂点カラー値 C21 (1,1,1,1)
頂点22の頂点カラー値 C22 (1,1,1,1)
頂点23の頂点カラー値 C23 (1,1,1,1)
頂点カラーの解説
この図には赤色のグラディエーションが付いた絵柄が表示されておりますが、その絵柄のデータはこのglbファイルには格納されておりません。
ビューアソフトが頂点カラーデータを読み取って画像処理しているようです。
このファイル作成に使用したBlenderでもそのようなグラディエーションが付いた絵柄が表示されています。
テクスチャー
次はテクスチャーです。
前項の頂点カラーは頂点だけに色情報を付与していましたが、ポリゴン面に2次元画像を貼り付けることで3次元物体に画像情報を付与しています。
この方法は3DCG技術のテクスチャーマッピングと呼ばれ、glTFファイルではこの仕組みが採用されています。
具体例4
同じく、Blenderから出力された、単純な立方体モデルのglbファイル(バイナリ)を拝借してその構造を説明します。
テクスチャーありのモードにすると(Blenderでメッシュモードのノーマルあり、BaseColorのImage Textureに640×426画素のJPEG画像を指定)、glbファイルのバイナリデータには、24個の頂点の座標値と24個の法線ベクトルと36個のインデックス(indices)データと24個のテクスチャー座標値と、最後尾にそのJPEGファイルデータがコピーされて格納されます。
複数の分離されたファイルからなるgltfファイルの場合には、そのJPEGファイルはファイルのままコピーして添付されます。
glbファイルのJSONデータには、ジオメトリを示す"meshes"情報の"primitives"は、
"primitives"
{
"attributes":
{
"POSITION":0,
"TEXCOORD_0":1,
"NORMAL":2
},
"indices":3,
"material":0
}
となっています。これにより、頂点の座標値と法線ベクトルとインデックス(indices)データとテクスチャー座標値データが格納されることが示され、所定の場所からそれらデータが取り出されます。
頂点の座標値と法線ベクトルは具体例2と同じですので、テクスチャーデータのみ以下にデータを示します。
テクスチャー座標値
各頂点に対し、
バイト数 データ型
8Byte float × 2
計24個×8=192Byteのデータが格納されています。
この24個のテクスチャー座標値(TEXCOORD_0値)は、上記具体例2の24個の頂点に付加されています。テクスチャー座標値0、1、2、・・・の番号は具体例2と同様にそれらテクスチャー座標値データの格納順で、頂点座標値の番号と対応しています。
記号Tは筆者が適当に付与した記号です。
テクスチャー座標値は水平、垂直それぞれ0から1の値(水平座標値、垂直座標値)で表される2次元平面上の座標値です。左上端が(0,0)、右下端が(1,1)になっています。
テクスチャー座標値は24個の頂点に対して付与されるので、頂点座標値と同様に、3個のテクスチャー座標値が重複して記録されています。
具体例4のデータ
TEXCOORD_0
頂点0texture座標値 T0 (0.625,0.5)
頂点1texture座標値 T1 (0.625,0.5)
頂点2texture座標値 T2 (0.625,0.5)
頂点3texture座標値 T3 (0.375,0.5)
頂点4texture座標値 T4 (0.375,0.5)
頂点5texture座標値 T5 (0.375,0.5)
頂点6texture座標値 T6 (0.625,0.25)
頂点7texture座標値 T7 (0.625,0.25)
頂点8texture座標値 T8 (0.625,0.25)
頂点9texture座標値 T9 (0.375,0.25)
頂点10texture座標値T10(0.375,0.25)
頂点11texture座標値T11(0.375,0.25)
頂点12texture座標値T12(0.625,0.75)
頂点13texture座標値T13(0.625,0.75)
頂点14texture座標値T14(0.875,0.5)
頂点15texture座標値T15(0.125,0.5)
頂点16texture座標値T16(0.375,0.75)
頂点17texture座標値T17(0.375,0.75)
頂点18texture座標値T18(0.625,0)
頂点19texture座標値T19(0.625,1)
頂点20texture座標値T20(0.875,0.25)
頂点21texture座標値T21(0.125,0.25)
頂点22texture座標値T22(0.375,0)
頂点23texture座標値T23(0.375,1)
テクスチャーの解説
【図6】で茶色線で示した6個の四角形(この中に2個のポリゴン三角形がある)がテクスチャー領域であり立方体の6個の面に対応しています。
このテクスチャー領域に描かれた画像が立方体の各面に貼り付けられることになります。
【図7】はテクスチャー領域と指定した640×426画素のJPEG画像との対応関係を示しています。
この場合は、テクスチャー領域の画像が切り出されて貼り付けられます。
白熊の左耳が貼り付けられていないことが分かると思います。
その部分がテクスチャー領域に入っていないためです。
Blenderにはあらかじめテクスチャー座標を表示するか保存させて、好みの画像を編集設定する機能がありますが、今回は単純な画像ファイル指定だけにしています。
テクスチャーマッピングする2次元画像を如何にして立体物に望む形に貼り付けるかが課題ですが、glTFファイルでは貼り付ける2次元画像データを立体物のデータと分離して、対応関係を示すデータを格納するという方法を採っていると考えることができます。
glTFファイルのカメラデータを調べるなら
おわりに
などを学びました。
などの3DCG技術で重要な機能があります。これらはどれも画像データに直接関係はしませんが、いずれも興味深い技術ですので調べてみたいと思っています。
引用:VRM
引用:人物・キャラクターに特化した3Dデータのファイル形式「VRM」
などの記事があり、glTFファイルをベースにしてその機能を取捨選択しているとされています。
glTFファイルフォーマットは3DCGのいろいろな構造を広範にサポートできるフォーマットですが、glTFファイルを扱うソフトにはその多様さに対応する必要が生じます。
分かり易い例として、glTFファイルに格納されている法線ベクトルを利用するソフトである場合に、具体例1のような法線ベクトル情報がないファイルに対しては、法線ベクトルを計算する機能を別途備える必要が出てきます。
都合良くglTFファイルを扱うにはそういう取捨選択も必要になるのでしょう。