780Mで2Dスプライトを3Dメッシュに変換する
この780M(Radeon 780M、gfx1103)だけを使ったローカルの、クラウドを一切使わないパイプラインで、平面の2Dゲームスプライトを使える3Dキャラクターモデルに変えられるか。それがここでの問いでした。基準にしたのはパイプラインとして成立するかどうかで、Craymel Ballにすでにあった素材、P1・P2・ボールのスプライトが、そのまま実地のテストケースになりました。
ツールの変遷
最初に試したのはTripoSRで、これは不採用にしました。既存の2Dスプライトをそのまま流し込むと、パイプライン自体はつまずかずに最後まで走りましたが、出てきたジオメトリは使い物になりませんでした。粗いけど使えるというレベルではなく、何の形かもわからない塊です。
Hunyuan3D-2miniが最初に実用になった段階でした。kijai/ComfyUI-Hunyuan3DWrapperとhunyuan3d-dit-v2-mini.fp16.safetensorsを入れて、形状だけのパイプライン(Hy3DModelLoader → Hy3DGenerateMesh → Hy3DVAEDecode → Hy3DPostprocessMesh → Hy3DExportMesh)を走らせました。テクスチャ・ペイントの段階は丸ごとスキップしています。CUDA専用のcustom_rasterizer/nvdiffrastが必要で、ROCm向けのビルドがないためです。フロント画像1枚だけの入力(518×518、フラットなグレーの上に合成)でも、出てきたものは元のキャラクターだとはっきりわかる出来でした。このPOCが「一応成立しそうだ」と感じられたのは、この段階に来てからです。
Hunyuan3D-2mvで、画質は一段階跳ね上がりました。P2のジオメトリに実測できる限界(後述)が出たのをきっかけに、mini版ではない標準のHunyuan3D-2mvチェックポイント(ダウンロードサイズはおよそ4.9GB)に切り替え、画像1枚の代わりに正面・背面・左・右の実際の4方向画像をHy3DGenerateMeshMultiViewに渡すようにしました。入力の受け取り方自体がアーキテクチャレベルで違い、モデルサイズが大きくなっただけではありません。ラッパーのconditionerのコードを読むとそれが裏付けられていて、ビュー方向ごとに学習された本物の位置埋め込みが入っていて、ダミーの代用品ではありませんでした。
ローカルでのROCm生成が浮き彫りにしたこと
単一視点の入力は、奥行きを黙って不定にする。 これが最大の教訓でした。P1は正面画像1枚から自然な奥行き/高さ比0.71が出ましたが、P2は0.34で、見るからに平たいものになりました。マントのシルエットは、体に沿ったジャケットに比べて奥行きの手がかりをずっと弱くしか与えないからです。単一画像のパイプラインは、こうなっていることを教えてくれません。出力を測ってはじめてわかります。最初の対処は、アセットごとに手で決めるZ軸の伸長係数でした。効く修正ではありますが、数値の形をした当て推量でもあります。マルチビュー入力は、この問題を根っこから解決しました。手動補正なしで0.34から0.55まで上がったのは、モデルが推測ではなく実際の背面・側面のピクセルから再構成できるようになったからです。
形状だけの出力は、そのままテクスチャの問題を引き継ぐ。 ROCmの制約でペイント段階を飛ばすと、どのメッシュもフラットなグレーのまま出てきます。新しくAIでテクスチャを生成し直す代わりに、ゲーム自体のスプライト素材を直交投影で頂点カラーとして流用しました。この方法で使える素材を作る上で、つまずいた点が2つありました。
- 頂点カラーの中に方向性のあるシェーディングを焼き込んだ上で、リアルタイムレンダラーにもメッシュを照らさせると、二重に暗くなります。焼き込むのは色だけにして、シェーディングはエンジン側のライティングに任せるのが正解でした。
trimeshのGLBエクスポートは、頂点カラーのみのメッシュに対してマテリアルをまったく書き出しません。そのためglTFのビューア側はmetalness: 1、roughness: 1というほぼ真っ黒なデフォルトにフォールバックします。頂点カラーだけのパイプラインでは、エクスポート後に明示的にディフューズマテリアルを付け直す必要があり、原因にたどり着くまでに、まとまったデバッグの時間を食われました。
マルチビューのconditioningには、ドキュメント化されていないと感じるくらい細かい入力の作法がある。 求めているのは本物のアルファチャンネルか、あらかじめ黒背景に合成済みの画像のどちらかで、単一視点でうまくいっていたフラットなグレーとは違います。ここを間違えてもエラーにはならず、出力の質が黙って落ちるだけです。
解像度を上げても、自動的に細部が増えるわけではない。 marching cubesの細かさを上げようとoctree_resolutionを384から512に上げてみましたが、この用途では行き止まりでした。増えた分の細部は、すぐ下流にある固定40kフェイスのデシメーションで削られ、さらにゲーム側の約8kフェイスへのデシメーションでもう一度削られます。計算量が6倍になっても、結果は測定上ほぼ同じでした。デシメーションを挟むパイプラインで「解像度を上げれば得」と決めてかかる前に、知っておく価値のある話です。
ローカル環境は、実験の途中で噛みついてくるくらいには脆い。 止まったように見えるジョブ(CPU使用率0%、割り込みにも反応なし)は、実はハングではなく単に遅いだけでした。ですが、早まって再起動したことで、ComfyUIのPython環境の依存関係が欠けていることが表面化しました。それを直そうとpip install -r requirements.txtを無条件にかけたところ、ROCm対応のtorchビルドが黙ってCPU専用のPyPIホイールに入れ替わっていました。torch.version.hipを確認したことで実害が出る前に気づけて、肝心のvenvは無事だとわかりました。ローカルで生成を繰り返す人には知っておいてほしい落とし穴です。環境が完全に隔離されていないと、普段どおりの依存関係インストールだけでGPUアクセラレーションが静かに壊れます。
結論
ローカル生成した形状だけのメッシュは、このハードウェアでもスタイライズされたローポリのゲーム素材として十分に使える道でした。それとわかる、比率もそれなりに整ったキャラクターが、クラウド依存なしで手に入ります。品質を左右したのは視点の数で、モデルに渡す実際の角度を増やすことが、解像度を上げることや後処理よりも数値を動かしました。今の粗さはパイプラインの成熟度の問題で、モデル自体の限界はまだ見えていません。扁平化はパイプラインの中で自動検知される仕組みがなく、手で測るしかありません。マルチビューの入力仕様は、どこにもまとまったドキュメントがありません。そして、共有しているローカル環境は、依存関係の変更に対して相応の注意が必要です。