GLB と FBX:ゲームエンジンに渡すべきなのはどちらか
glTF/GLB と FBX が実際に何を運ぶのか、どこで静かにデータを失うのか、Unity・Unreal・Godot・Blender・Web には結局どちらを渡すのか。
どちらの形式でもメッシュはエディタからエンジンへ渡ります。争点になるのはメッシュに付いてくる残り全部です。マテリアルモデル、テクスチャの参照のされ方、数値がどの単位なのか、どちらが上なのか、そしてリグが道中で生き残るのか。これらの違いはどれも具体的で、たいていは仕様として書かれています。つまりこれは好みではなく、答えのある問いです。
この二つは実際のところ何なのか
glTF 2.0 と GLB
glTF 2.0 は Khronos Group によるオープンでロイヤリティフリーの仕様です。Vulkan や OpenGL を作っているのと同じ団体です。ランタイム配信用の形式として設計されていて、ファイル内のデータはすでに GPU が欲しい形に近く、ローダーは変換よりもバッファのアップロードを主に行います。コンテナは二種類あります。.gltf は JSON で、ジオメトリのバッファや画像は別ファイルとして隣に置かれるか、base64 で埋め込まれます。.glb は同じ JSON と一つのバイナリ塊を単一ファイルにまとめたものです。base64 はバイナリを三割ほど膨らませるので、実際に配るのは GLB です。
この仕様は、FBX が開いたままにしている部分を釘で留めています。線形距離はすべてメートル。座標系は右手系で +Y が上、+Z が前。角度はラジアン。マテリアルは metallic-roughness で、しかも拡張ではなくコア仕様に入っています。
FBX
FBX はモーションキャプチャ用アプリケーション Filmbox のファイル形式として Kaydara で生まれ、2006 年に Autodesk が取得しました。ランタイム形式ではなくオーサリングと交換のための形式であり、振る舞いもその通りです。NURBS サーフェス、コンストレイント、LOD グループ、複数のアニメーション take、カメラ、ライトまで運べます。
公開仕様はありません。リファレンス実装は Autodesk の FBX SDK で、クローズドソースのバイナリとして配布されています。だから GPL のツール、たとえば Blender は、自前のリーダーをゼロから書くしかありませんでした。ファイルはバイナリと ASCII の二種類があり、拡張子はどちらも .fbx。しかも形式にはバージョンがあります。FBX 2020 のファイルはバージョン 7.7 で、2014 年ごろの SDK でビルドされたツールが 7.7 を読むと、アニメーションやカスタムプロパティを黙って落とすことがあります。「FBX で書き出す」は一つの行為ではありません。
項目ごとの比較
| glTF 2.0 / GLB | FBX | |
|---|---|---|
| 管理主体 | Khronos Group、オープンでロイヤリティフリーの仕様 | Autodesk、公開仕様なし。参照実装はクローズドな FBX SDK |
| コンテナ | .gltf(JSON と外部ファイル)または .glb(単一バイナリ) | .fbx、バイナリまたは ASCII、バージョン番号付き |
| マテリアル | コア仕様の metallic-roughness PBR | Lambert と Phong。PBR はベンダー慣習で運ばれる |
| テクスチャ | コアは PNG と JPEG、KTX2 は拡張。GLB には内包 | DCC が書き出す任意の形式。Embed Media を入れたときだけ内包 |
| 単位 | メートル、仕様上の必須 | ファイルの UnitScaleFactor 次第。既定はセンチメートル |
| 軸 | +Y が上、+Z が前、右手系、必須 | ファイルごとに GlobalSettings で宣言。アプリごとに異なる |
| アニメーション | ベイク済みのノードとモーフ重みのトラック、step / linear / cubic spline | take、コンストレイント、ブレンドシェイプ、カメラとライトのアニメーション |
| 独自データ | ほぼどのオブジェクトにも付けられる extras の JSON | カスタムユーザープロパティ。Unity と Unreal にインポートフックあり |
| 圧縮 | ジオメトリは Draco と meshopt、テクスチャは KTX2 | 形式としては持たない |
| エンジン対応 | Godot はネイティブ、Unreal は Interchange、Unity はパッケージ、Web では既定 | Unity はネイティブ、Unreal は長年の実績、Godot と Blender は ufbx |
それぞれ、どこでデータを失うのか
マテリアル、ここが本当の差
glTF のコアマテリアルは metallic-roughness で、チャンネルの割り当ても決まっています。metallic-roughness テクスチャの緑にラフネス、青にメタルネス、赤にアンビエントオクルージョンが入るので、一枚のパックしたテクスチャで三つをまかなえます。ベースカラー、ノーマル、エミッシブにはそれぞれ専用のスロットがあります。ここに「マッピング」する余地はありません。Blender や Substance で作った metallic-roughness マテリアルは、metallic-roughness マテリアルのまま届きます。
FBX が定義しているのは Lambert と Phong です。どちらも PBR ではありません。PBR を FBX に書き込むエクスポーターはすべて、仕様ではなく慣習に従って書いています。もっとも多いのは Autodesk の Stingray PBS というプロパティ名前空間です。そして読み込む側は、書き出した側が使った慣習をその都度言い当てなければなりません。FBX が届いてラフネスマップがスペキュラのスロットに刺さっていたり、metallic と roughness が単に空だったりするのは、この食い違いが起きたということです。二つの形式のあいだで最も大きな実務上の差であり、「モデルがグレーで入ってきた」というスレッドの大半が存在する理由でもあります。
内包テクスチャと参照テクスチャ
GLB は構造として内包します。画像はジオメトリと同じバイナリバッファに入るので、一つのファイルでアセット全体が動きます。コアの glTF が許すのは PNG と JPEG。KHR_texture_basisu 拡張が Basis Universal データを収めた KTX2 コンテナを追加し、こちらは生の RGBA に展開されずビデオメモリ上で GPU 圧縮のまま保持されます。
FBX は既定では参照です。テクスチャはパスであり、パスはマシンをまたいだ時点で切れます。Maya、3ds Max、Blender のエクスポーターにある Embed Media を有効にすると画像が FBX の中に入り、インポート時にはファイル名に .fbm を付けたフォルダが隣に作られて展開されます。代償はサイズで、4K のマップが入った途端に同じ書き出しが約 1 MB から数十 MB になります。多くのエクスポーターで Embed Media は既定でオフです。届いた FBX がピンク一色になる原因はここにあります。
スキニング、アニメーション、カスタムプロパティ
glTF が保存するのはベイク済みの結果です。アニメーションはノードの移動・回転・スケールとモーフ重みのトラックで、補間は step、linear、cubic spline のいずれか。スキンの影響は四つ一組で入ります(JOINTS_0 と WEIGHTS_0)。組を増やすこと自体は仕様上可能ですが、リーダー側の対応はまちまちで、Unreal の Interchange glTF インポーターはファイルの中身にかかわらず頂点あたり四影響に切り詰めるという報告があります。IK やコンストレイント付きのリグを glTF に通せば、返ってくるのは動きであってリグではありません。
FBX は逆にオーサリングのデータを運びます。複数の take、ブレンドシェイプ、コンストレイント、アニメーション付きのカメラとライト。独自データについては、形勢が少しだけ逆転します。glTF には extras があり、ノードやメッシュやマテリアルに任意の JSON を付けられます。形式としては完全に保持されますが、フックを書かないかぎり大半のエンジンのインポーターは無視します。FBX のカスタムユーザープロパティのほうがエンジンへの道は踏み固められていて、Unity は OnPostprocessGameObjectWithUserProperties で拾えますし、Unreal には FBX メタデータのパイプラインがあります。DCC の段階でノードにスポーン地点やゲームプレイの値をタグ付けするなら、FBX のほうが手間は少なくて済みます。
九十度回っている、あるいは百倍大きい
これは一つのバグに二つの原因があるという話で、どちらもファイルの破損ではありません。二つのプログラムが約束事で食い違っただけです。
単位。FBX ファイルは GlobalSettings ブロックに UnitScaleFactor を持ち、形式の基準単位はセンチメートルです。係数 1 はセンチメートルを意味します。メートル基準のシーンで作った 2 m のドアを、数値をそのままにして書き出すと、単位はセンチメートルだと宣言しているファイルの中で「2」になります。ヘッダを信じるリーダーは 2 cm のドアを、無視するリーダーは 2 m のドアを返します。Unity の FBX インポーターが Scale Factor の既定値を 0.01 にしているのは、まさにこのためです。glTF に相当するつまみはありません。メートルは設定ではなく要件だからです。この問題の鏡像がワールド単位をセンチメートルとする Unreal で、メートルの GLB は取り込みのどこかで 100 倍される必要があります。建物が百分の一の大きさで現れたら、その掛け算が抜けたということです。
軸。glTF は +Y を上と定めています。FBX は up と front の軸をファイルごとに宣言し、アプリの既定は一致しません。3ds Max と Blender は Z 上、Maya と Unity は Y 上です。Blender の glTF エクスポーターには既定でオンの +Y Up オプションがあり、変換を代わりにやってくれます。FBX エクスポーターのほうは Up と Forward を自分で選ばせる形で、選択が読み手の前提と食い違えばアセットは仰向けに倒れます。Unity では通常、インポートされたルートに -90 度の X 回転が焼き付いた状態として現れ、以後オブジェクトの前方ベクトルを読むスクリプトすべてと喧嘩します。
ほかの何より先に二つ。インポートしたものの横に 2 m の参照キューブを置くこと、そしてルートオブジェクトの回転値を見ること。この二つで「書き出しが壊れている」と報告される事案の大半は拾えます。
エンジン別に、何を書き出すか
Unity
FBX がネイティブの経路で、Unity は何も入れずに読めます。glTF のインポートは Unity glTFast、パッケージ名 com.unity.cloud.gltfast を通ります。エディタでのインポートとランタイムの読み込みの両方を、built-in・URP・HDRP でこなしますが、あくまでパッケージなので誰かが入れる必要があります。プロジェクトに glTFast がなく入れる予定もないなら FBX。あるなら GLB、とりわけランタイムでモデルを読み込むものがあるなら、自己完結した一ファイルでマテリアルが最初から PBR というのは手間がまるで違います。生成された環境まるごとでも同じ判断になります。マテリアル数だけで FBX の慣習問題は高くつくからです。
Unreal Engine
Unreal はどちらも受け取ります。FBX のほうが歴史が長く、周辺のツールも厚い。glTF は現在 Interchange フレームワーク経由でインポートされ、そのプラグインは既定で有効です。そして Epic は旧来の glTF Importer と Datasmith glTF Importer を将来的な削除対象としています。向かっている方向は明白です。マテリアルでは glTF が有利で、metallic-roughness は解釈し直さずに Unreal のモデルへ載ります。スケルタル系の作業は今のところ FBX が有利で、理由の一部は先ほどの影響数の制限です。Unreal は Z 上・左手系・センチメートルであることも覚えておいてください。Epic は UEFN を起点に Y 上の右手系へ段階的に移行する意向を示していますが、今日のエディタは依然として Z 上です。「環境とプロップは GLB、キャラクターは FBX」という切り分けは今も有効です。
Godot
ドキュメントには glTF 2.0、推奨、とそのまま書かれています。FBX は Godot 4.3 以降、オープンソースの ufbx ライブラリでネイティブに読み込めます。それ以前は FBX2glTF という、Autodesk のプロプライエタリな SDK にリンクした外部バイナリを別途入れる必要がありました。Godot は Y 上・右手系・メートルで、glTF が定める約束事とそのまま一致するので、取り込み時に変換は起きません。GLB、書き留めるほどの但し書きもなしに。
Blender
どちらも往復できますが、質は同等ではありません。glTF のインポーターとエクスポーターは Khronos と共同で作られ、Blender に同梱されています。FBX はライセンス上 Autodesk の SDK を使えたことが一度もありません。インポーターは ufbx ライブラリを土台に C++ で書き直され、Blender 5.0 で既定になりました。エクスポーターは今も Python 実装のままです。GLB を使ってください。ただし相手が Maya や 3ds Max で作業している人なら、FBX がその人の母語です。
Web と three.js
ここは勝負になりません。GLTFLoader が維持されている推奨経路で、FBXLoader は存在はしますが渡されたファイルを必ず開けるわけではありません。GLB 一つはリクエスト一回で、依存の取りこぼしがありません。ジオメトリには Draco か meshopt、テクスチャには KTX2 を足してください。スマートフォンで動かすなら、こちらのほうがダウンロードサイズより効きます。生成シーンの削り込みはそれ自体で別の仕事で、判断の種類も違います。
両方書き出すのが答えになるとき
同じソースからもう一度書き出すのは数秒の作業で、それが端的に正解になる場面はいくつもあります。
- 受け取り側のパイプラインを自分で決められない。ストアへの出品、クライアントへの納品、他スタジオへの受け渡し。両方渡して選んでもらいましょう。
- モデルの行き先が二つある。エンジンのビルドと、Web ビューアや configurator では、同じアセットに求めるものが違います。
- 部位ごとに行き先が違う。環境は GLB でエンジンへ、キャラクターは FBX でアニメーターの手元へ。
- インポートの不具合を切り分けている。GLB がきれいに入って FBX が入らないなら、原因は FBX のマテリアル慣習であってメッシュではありません。半日が浮きます。
Cuberta はテクスチャ込みで GLB または FBX を書き出すので、生成したロケーション全体でこの比較を回すのは作り直しではなく二分の実験で済みます。プロジェクトごとに一度、しかも早いうちに、四百個のオブジェクトが仮定の上に乗ってしまう前にやっておく価値があります。
ファイルが届いた瞬間に見るもの
この順に二分眺めるだけで、ほとんどは捕まります。
- スケール。横に 2 m の参照キューブを置く。ドアと階段がいちばん早く白状します。
- 向き。インポートされたルートの回転はゼロであるべきです。X に -90 が付いていれば軸の約束事が食い違っています。
- マテリアル数。元と一致しているか、それとも全部が一つのスロットに潰れたか。
- テクスチャの割り当て。ベースカラーは sRGB、ノーマルとパック済みマップはリニア。グレーやマゼンタは参照が解決していない証拠で、FBX の隣の
.fbmフォルダを探します。 - シェーディング。滑らかであるべき場所が面で割れていたら、スムージンググループかタンジェントが渡っていません。タンジェントを明示的に書き出して再エクスポートします。
- スキニング。頂点あたりの最大影響数と、バインドポーズが元と一致しているか。
- 名前。階層の名前。スクリプトやプレハブバリアントがそれを手がかりにしていて、後からの改名は高くつきます。
ここまでの全部から出てくる結論は、議論そのものより退屈です。パイプラインの中に FBX でなければならない具体的な事情がないかぎり glTF と GLB を使う、そしてその事情を名指しできるようにしておく。2019 年なら正直な答えは「留保付きで FBX」でした。今は Godot が glTF をはっきり推奨し、Unreal は旧 glTF プラグインを畳んで一級のインポーターへ寄せ、Unity は自前のパッケージを出しています。FBX が消えることはありません。オーサリングアプリ同士が話す言語であり続けますし、glTF では記述できないリグを運びます。それでもランタイムに渡すものとしては地歩を失い続けています。理由はフォーラムの言い合いではなく、仕様書のほうに書かれています。