生成した3DロケーションをUnityへ:GLBから遊べるシーンにするまで

生成したGLBやFBXのロケーションは数分でUnityに入ります。遊べる状態にするのはインポート設定、コライダー、ライティングです。

Cuberta でエージェントが構築した町。通り、横断歩道、れんが造りの集合住宅とコテージ

町ひとつ分の60 MBのGLBを Assets フォルダに置いても、Unityは無反応です。あるいは読み込まれて、建物がコイン大になっています。あるいはサイズは正しいのに全部がマゼンタで、キャラクターは歩道をすり抜けて落ちていきます。これが、生成されたロケーションを触りはじめて最初の二十分の定番で、そのすべての段階に名前のついた設定が対応しています。

以下は、ロケーションのGLBかFBXが手元にあり、ほぼ空のUnityプロジェクトがあることを前提にしています。目指すのはスクリーンショットではなく、歩き回れるシーンです。

まずファイルをプロジェクトに入れる

Unityのモデルインポーターは、標準でFBX、OBJ、DAE、DXFを読みます。glTFとGLBは読みません。組み込みのインポーターが存在しないので、新規プロジェクトに .glb をドラッグしても、ただのファイルとして置かれるだけです。インポーターはパッケージで足します。

そのパッケージが Unity glTFast で、Package Manager の一覧に名前では出てきません。Window > Package Manager+ ボタン、Add package by name と進み、com.unity.cloud.gltfast を入力します。近年のリリースは Unity 2021.3.46f1 以降を要求し、Built-in、Universal、High Definition の各パイプラインに対応します。導入後は .gltf.glb も他のモデルと同じで、メッシュ・マテリアル・テクスチャをサブアセットに持つプレハブとして入ってきます。

ツール側で書き出し形式を選べる場合、判断材料は形式そのものより、インポートしたあとに何をするかです。たとえば Cuberta はGLBもFBXもテクスチャ込みで書き出すので、ここはドロップダウンの選択であって、変換という工程ではありません。

必要なこと書き出し理由
ライトマップをベイクするFBXUnity純正のインポーターが第2UVを生成できる。glTF系インポーターはこの点が安定しない
テクスチャがファイルと一緒に来るGLBバイナリの中にあるので、貼り直すパスがない
数百のマテリアルスロットを一括で差し替えるFBXMaterialsタブに Search and Remap がある
反復作業のループを最短にするGLBファイル1つ出して1つ入れるだけ。同期すべきテクスチャフォルダがない

形式そのものの比較は別の話題です。ここではどちらでも通ります。上の表は、あとで手間が少ないのはどちらか、という話です。

スケールと、九十秒でそれを確かめる方法

百倍ずれる問題

Unityの物理はワールド単位の1を1メートルとして扱い、コントローラーも重力値もドラッグの設定もこの約束の上に乗っています。FBXはファイル内に独自の単位スケールを持っていて、センチメートルで書き出すツールは少なくありません。Modelタブの Convert Units はその宣言されたスケールを読んで換算します。Scale Factor は、宣言そのものが間違っていたときの手動の倍率です。町が百倍大きいという事故は、ほぼこの二つのフィールドに帰着します。同じタブの Bake Axis Conversion は、上方向の軸の補正を回転したルートオブジェクトに隠さず、メッシュデータへ焼き込みます。

glTFはメートル指定なので、GLBのスケールが狂うことはめったにありません。ただしglTFは右手系、Unityは左手系で、インポーターは片方の軸を反転させて辻褄を合わせます。町の見た目は正しいのに鏡像に読める——文字が裏返る、右へ曲がっていた道が左へ曲がる——なら、見ているのはそれです。

目で測らず、キャラクターコントローラーで測る

インポート結果の上に何かを積む前に、シーンにCharacterControllerを置いてください。height 2、radius 0.5、step offset 0.3、slope limit 45。移動コードを十行ほど書いて、実際に歩きます。次の四点でほぼ判定できます。

  • 戸口が2mのカプセルを頭上に余裕をもって通す。こすらない。
  • 縁石が0.1〜0.15mで、ジャンプなしでstep offsetが上げてくれる。
  • 階段の蹴上げがstep offsetより低い。でなければどこにも登れません。
  • 車線の幅が3〜3.5mで、横断に三歩ほど。

戸口を合わせるためにScale Factorを0.35にする必要があり、その結果として歩道が滑走路のように見えるなら、問題は単位ではなく比率で、どのインポート設定でも直りません。

マテリアル:何が生き残り、何を作り直すか

シェーダーは今いるパイプラインが決める

ベースカラー、metallic-roughness、ノーマル、オクルージョン、エミッシブの各マップ、アルファモード、両面フラグ、頂点カラーはそのまま渡ってきます。渡ってこないのはエンジン固有のものすべてで、シェーダーグラフ、カスタムシェーダー、デカールプロジェクター、テレインレイヤー、トリプラナーの構成などがそれにあたります。これらはUnity側で作り直すか、諦めるかです。

さらにインポーターは、インポート時点で有効なレンダーパイプラインに合わせてシェーダーを選びます。Built-inのプロジェクトでGLBを読み込んでからURPへ切り替えると、そのファイル由来のマテリアルはすべてマゼンタになります。出口は二つ、シェーダーの出自で決まります。

  • Unity標準のシェーダー——StandardやURP/Litに載るFBXの典型例です。Window > Rendering > Render Pipeline Converter を開き、変換元と変換先のパイプラインを設定してマテリアルコンバーターを実行します。カスタムシェーダーは対象外で、変更は元に戻せないので先にコミットしておきます。
  • glTFインポーター独自のシェーダー——コンバーターに対応表がありません。先にパイプラインを整えてから、アセットを右クリックして再インポートします。

手でマテリアルを組むならチャンネルを見る

glTFは1枚のテクスチャの緑チャンネルにroughness、青にmetallicを詰め、オクルージョンが同居する場合は赤に入ります。UnityのStandardシェーダーが求めるのは赤のmetallicと、アルファのsmoothness——つまり1マイナスroughnessです。glTFのmetallic-roughnessマップをURP/LitのMetallicスロットに入れると、チャンネル違いと反転で二重に間違った表面ができます。インポーター独自のシェーダーはglTFの並びをそのまま読むので、「整理のために」URP/Litへ差し替えると町全体がプラスチックに見えるようになります。

黙って失敗するテクスチャのフラグが二つあります。ノーマルマップはテクスチャタイプをNormal Mapにすること。カラーデータ扱いされたノーマルマップは壊れて見えず、平坦に見えるだけです。そしてパック済みマスクはsRGBのチェックを外すこと。外さないとガンマとして解釈され、全体がわずかに光沢過多になります。FBX側では、これらはすべてMaterialsタブにあります。Locationを Use Embedded Materials にし、Extract MaterialsExtract Textures を押してから、Search and Remap で数百のスロットを自前のライブラリへ一度に向け直します。

コライダー:メッシュコライダーは外す

生成ジオメトリとMesh Colliderの相性が悪いのは好みの問題ではなく、エンジンの硬い制限のためです。メッシュコライダーは凸か、そうでないかのどちらかです。非凸は静的専用で、Rigidbodyを付けるとUnityが拒否します。凸は255三角形が上限で、生成された家は三千から三万あります。つまり建物の表示メッシュをコライダーにできるのは、その建物が決して動かない場合だけで、それでもなお、壁にぶつかったと伝えるためにファサードの全三角形を物理に舐めさせていることになります。

インポート時に Generate Colliders を切り、意識的に一巡します。

  • 地面、道路、地形——おおむね平らな一枚ものなら、表示メッシュに非凸のMesh Colliderで許容範囲です。低ポリの当たり用メッシュを別に用意すればなお良く、平坦な街区なら下に箱を一つ敷くのが最も軽い。
  • 中に入らない建物——フットプリントごとにBox Colliderを一つ、レンダラーのバウンズに合わせます。
  • 中に入る建物——壁・床・天井ごとに箱を置き、戸口は箱と箱の隙間として作ります。作業は遅くなりますが、実行時のコストは桁違いに安くなります。
  • 樹木、街灯、標識、ボラード——幹やポールにCapsule Colliderを一つ、樹冠には何も置かない。枝の下は通れて当然だとプレイヤーは思っています。
  • 縁石や低い段差——多くの場合は何も要りません。step offsetが越えさせてくれるので、そこにコライダーを置くとつまずくだけです。
  • Rigidbodyが付くもの——プリミティブか、255三角形未満の凸メッシュ。第三の選択肢はありません。
コライダーが記述するのは、プレイヤーがどこへ行けるかであって、そのオブジェクトがどう見えるかではありません。大聖堂にも箱で十分です。

四百オブジェクトなら、この一巡はスクリプトにします。階層をたどり、各RendererのバウンズからBoxColliderを付け、植生の名前接頭辞は飛ばす。それだけで一分足らずで大半が片付きます。成立する条件は、ロケーションが溶接された一枚のメッシュではなく個別のオブジェクトとして届いていることで、書き出し前に確認する価値があります。Cuberta のようなエージェント駆動のエディタでは、建物もプロップも選択できるオブジェクトのまま出てきます。ついでに Player Settings の Prebake Collision Meshes は有効のままに。実行時にメッシュコライダーをクックすると、シーン読み込みの瞬間のフレーム落ちとして表に出ます。

ライティング:足りない第2UV

生成メッシュのほとんどはUVを1セットしか持ちません。テクスチャが使っているものです。ライトマップには2セット目が要り、しかも単位正方形の中でどの三角形も重ならないように展開されている必要があります。それがないと、ベイク結果は黒い斑になり、継ぎ目が出て、隣の壁から光が漏れます。

FBXならチェックボックス一つで済みます。モデルのインポート設定にある Generate Lightmap UVs が第2チャンネルを作ります。三百のメッシュに対して入れる前に、代償を把握しておくべきです。

  • インポート時間。数百メッシュの展開には数分かかり、再インポートのたびに走り直します。
  • テクセル効率。自動パッキングは手作業の展開より緩いので、同じ品質に対してライトマップ解像度を余計に使います。
  • ジオメトリは直りません。元データの重複三角形や退化三角形は、展開したあとも漏れ続けます。

glTF系インポーターはライトマップUVの生成について挙動が一定せず、そもそも用意されていないこともあります。ベイクすると分かっているならUnityへはFBXで持ち込む、その現実的な理由はここにあります。GLBで通すなら、各メッシュに対して Unwrapping.GenerateSecondaryUVSet を呼ぶエディタスクリプトで自分で作ります。

展開ツールと格闘する前に、代案を一つ検討してください。Unity 6 の Adaptive Probe Volumes は、ライトマップUVも手作業のプローブ配置も要らずに、URPとHDRPへベイク済みの間接光を与えます。Unityがジオメトリの密度に応じてプローブを配置します。どのみちUVを制御できない生成ロケーションでは、こちらのほうが割の良い取引であることが多い。手放すのは静的な面に乗る細かなベイク影で、プローブが運ぶのは間接光であって、くっきりした接地影ではありません。

大きなロケーションが痛みはじめる場所

四百オブジェクトは、キャラクターが入る前から四百ドローコールです。生成された町はこの数字にすぐ届きます。効くのは主に三つの設定です。

  • Staticフラグ。動かないものはすべてStaticにします。これで静的バッチング——Unityがメッシュを最大64,000頂点の共有バッファへまとめます——に加えて、オクルージョンカリングとGIへの参加が手に入ります。代償はメモリで、まとめられたバッファはジオメトリの追加コピーなので、固有メッシュだらけの町では数百MB増えることもあります。
  • GPUインスタンシング。ジェネレーターが同じ街灯を六十回使い回しているなら、そのマテリアルで Enable GPU Instancing を入れれば、六十個はごく少数のドローコールに畳まれます。固有ジオメトリの建物には効きません。優先順位に注意してください。Unityは静的バッチングに成功したレンダラーではインスタンシングを無効にするので、同一のプロップはどちらか一方になります。
  • Read/Write。実行時にメッシュデータを読む必要がなければ切っておきます。有効だと、各メッシュのコピーがCPUメモリにもう一つ残ります。

LODチェーン、テクスチャアトラス、三角形の予算は独立した主題で、生成シーンをゲーム向けに最適化するできちんと扱っています。

実際にぶつかる五つの失敗

  1. 全部マゼンタ。マテリアルが別パイプライン向けのシェーダーを持っています。目的のパイプラインを有効にして再インポートするか、Unity標準シェーダーなら Render Pipeline Converter を回します。
  2. 町が百倍大きい、または小さい。FBXの単位スケールです。Modelタブの Convert Units と Scale Factor を見て、目視ではなく2mのカプセルで確認します。
  3. キャラクターが地面をすり抜ける。コライダーがありません。glTFは一つも持ってこず、Generate Colliders はFBX側の設定で、おそらく意図的に切ったままです。コライダーの一巡をやります。
  4. ベイクが真っ黒、または斑。第2UVがありません。FBXなら Generate Lightmap UVs、GLBならエディタスクリプト、あるいは Adaptive Probe Volumes へ移ります。
  5. 何も置いていないのにPlayが12 fps。ドローコールと影です。Staticを付け、小物の影の投射を切り、当てずっぽうを始める前に Stats ウィンドウを読みます。

インポートそのものは二十分です。コライダーの一巡とライティングの判断が半日を持っていきます。手作業でレベルを組んでも同じ半日は消えたはずで、違いは、灰色の箱ではなく、戸口の高さも車線の幅も実寸の町から始められることです。一度きちんとやっておけば、以後の再インポートはすべて、最初に選んだ設定の上を走ります。