Houdiniを使って衣装の位置合わせを自動化したい
この記事はHoudini Apprentice Advent Calendar 2023の23日目の記事です。
初めまして、ねむり木です。普段VRChatで色々な創作をしています。
最近は別のアバター向けに作成された衣装を自分のアバターに半自動で着せ替えるHDAをちまちま開発しています。今日はこのHDAについて書こうと思います。
はじめに
現在、BoothではVRChat向け衣装が多く販売されています。これらのVRChat向け衣装は特定のアバターの体型に合わせて制作されており、自分の使っているアバターに対応していない衣装を着たい(着せたい)と思う場合がよくあります。
多くのユーザーが衣装を着せるためにUnityやBlenderを使って衣装の形状の調整を行いますし、制作者側で複数のアバターの対応作業を行ってから衣装を販売するケースも一般的です。このどちらの場合でも作った衣装を別のアバターの体型に合わせて形状を調整するという作業が発生しています。
今回はHoudiniでこの制作済みの衣装を別のアバターの体型に合わせて調整する作業の自動化を試みました。
注意事項として、キャラクターの出力に使用するROP FBX Character Output SOPはHoudini Apprenticeでは使用できずIndie以上のライセンスが必要となります。また記事の内容上、アバターの身体に対してブーリアン処理を行う場面があります。苦手な方は閲覧の際にご注意ください。
概要
今回の手法の概要を説明します。
この記事では元々の衣装が対応しているアバターの素体をソース素体、衣装を対応させたいアバターの素体をターゲット素体、対応させたい衣装を対象衣装と呼びます。
今回の手法は
- ソース素体とターゲット素体のそれぞれからアバターの体格を反映したラティスを作成する
- 上記のふたつのラティスを使用して、Point Deform SOPで対象衣装を変形させる
という流れで衣装の位置合わせをします。
1・2のそれぞれの処理に対応して、ラティス生成用HDACostume Fitting Lattice Generaterと衣装変形HDACostume Fitting Deformerのふたつを作成しました。以下ではそれぞれのHDAの面白い部分を紹介します。hipファイルを公開するので詳細を知りたい方はそちらをご確認ください。
Point Deform SOPとは
Point Deform SOPはメッシュ変形系のノードのひとつで、2番目の入力と3番目の入力の差分で1番目の入力を変形させます。BlenderでいうところのMesh Deformモディファイアに近いです。
HoudiniだとLattice SOPやCloth Deform SOPなど似たようなノードがいくつかありますが、Point Deformは変形される側がどんな形状でも良く、ラティス側も普通のメッシュが使用できるのでこれらの中でも癖なく使用できる印象です。これらのノードに共通する特徴として2番目の入力と3番目の入力の形状の接続性は一致している必要があります。
今回はソース素体とターゲット素体からそれぞれ2番目の入力(変形前のラティス)と3番目の入力(変形後のラティス)を作成し、その差分で対象衣装を変形させています。
Costume Fitting Lattice Generater
素体のメッシュとボーンを入力に取り、大まかな体型を表したラティスを出力するHDAです。
Point Deform SOPの入力に使用するため常に同じトポロジーを出力します。
ボーンの種類の判定
このHDAではまず各ボーンがUnityHumanoidAvatarのどのボーンに対応しているかを表すhumanoid_bone_type
アトリビュートを作成しています。
UnityHumanoidAvatarはUnityがキャラクターモデルを標準化するために定めているボーン構造で、VRChatで使用されるアバター・衣装はこの規格に沿って作成されています。UnityHumanoidAvatarについては以下の記事を参照ください
しかし個々のボーンは好きな名前を付けることができるため、ボーンのname
アトリビュートからそのボーンがUnityHumanoidAvatarのどのボーンに当てはまるのかを簡単に判定することができません。
そこで、アバターのそれぞれのボーンがUnityHumanoidAvatarのどのボーンに対応するかを判定するために、keyがUnityHumanoidAvatarのボーン名であり、valueが各ボーンの慣習的に使われる命名規則を網羅的に集めた配列であるdict型の変数bone_mapper
を用意しました。各ボーンでbone_mapper
の中からボーンのname
と一致する要素を持つ配列を探し、その配列と対応するkeyをhumanoid_bone_type
アトリビュートとして各ボーンに格納しています。(bone_mapper
の中身はModular AvatarのHeuristicBoneMapper.csを参考にしました)
dict get_bone_mapper(){ dict bone_mapper; bone_mapper["Hips"] = {"Hips", "Hip"}; bone_mapper["LeftUpperLeg"] = { "LeftUpperLeg", "UpperLeg_Left", "UpperLeg_L", "Leg_Left", "Leg_L", "ULeg_L", "Left leg","LeftUpLeg","UpLeg.L" }; ...... return bone_mapper; } string humanoid_bone_name = "None"; string target_bone_name = s@name; target_bone_name = normalize_name(target_bone_name); dict bone_mapper = get_bone_mapper(); string bone_keys[] = keys(bone_mapper); foreach(string bone_key; bone_keys){ string bone_names[] = bone_mapper[bone_key]; foreach(string bone_name; bone_names){ bone_name = normalize_name(bone_name); if(target_bone_name == bone_name){ humanoid_bone_name = bone_key; } } } s@humanoid_bone_type = humanoid_bone_name;
このセットアップを行うことでボーンの名前揺れに左右されずに素体のリグやウェイトを取得できるようになります。
体型の測定
このHDAでは、ラティスの形状を胴体の起伏に沿わせるために等高線を使ってアバターの3サイズを測定しています。
流れとしてはまず素体のメッシュからHip・Spine・Chestのウェイトが塗られているメッシュを切り出し、それぞれのメッシュからY軸方向の等高線を生成します。等高線の生成は堀川淳一郎さんのチュートリアルを参考にしました。
これらの等高線のそれぞれに対して、get_bbox
系の関数を使ってZ方向の最小値(min)・最大値(max)・厚み(size)を計測しアトリビュートとして格納します。
このアトリビュートを参考に、Hipメッシュの中で最小値が最も小さい(=一番後ろに突き出している)等高線をヒップ、Spineメッシュの中で一番厚みが薄い(=腰回りが細い)等高線をウェスト、Chestメッシュの中で一番厚みがある(=胸囲が長い)等高線をバストとして抜き出し、それぞれのBoundsを取りだします。
そしてこのBoundsの間に面を貼ることで胴体の大まかな起伏を表現したラティスの完成です。(以下では3サイズに加えて脇の下の位置に頂点を追加しました。)
Costume Fitting Deformer
衣装を変形させるためのHDAです。Character Pack SOPでPackされた対象衣装とふたつのラティスを入力に取ります。
基本はPoint Deform SOPのラッパーですが、キャラモデルの変形を行うための処理がいくつかあるので紹介します。
コントロールリグの設定(WIP)
こちらはまだ制作途中ですが、ラティスの形状を操作するためのリグコントローラーを組みました。
ラティスの持つ頂点からスケルトンを作り各部位ごとにコントローラーをアタッチすることで、腕や胴回りの位置を手動で調整できるようなセットアップになっています。また、ラティスの全てのポイントがスケルトンに含まれているので、変形の適応は(Bone Deformではなく)直接ボーンの位置をpoint wrangleで読み取って反映させます。
Blend Shapeの再構築
Point Deform SOPを使用して衣装の位置合わせをしても、衣装に付与されているブレンドシェイプは元の形状のままです。そのため変形した後にBlend Shapeの再構築が必要になります。
Blast SOPで@blend_shape_name!=""
を指定してBlend Shapeのプリミティブを選び出し、ForLoopを使用してBlend Shapeそれぞれに対して処理を行っていきます。
Blend ShapeはPacked PrimitiveなのでそのままではPoint Deform SOPによる変形はできません。ここでは各Blend Shapeごとに@blend_shape_name
を取り出して元のメッシュのdetailに格納し、その値を参照してCharacter Blend Shapes Extract SOPを行いBlend Shapeが適用された状態のメッシュを作ります。このメッシュに対してPoint Deform SOPによる変形を行い、Pack SOPでpacked primitiveに戻します。
最後にCharacter Blend Shapes Add SOPを使用して、変形させたBlend Shapeをまとめて衣装に設定しなおしてあげています。
Houdiniを使用してアバター改変を行おうとするとアバターのブレンドシェイプが破壊されるケースがよくあります。そのような場合に、私はブレンドシェイプに修正を加えるというよりも改変後のモデルにすべてのブレンドシェイプ作り直す方針で実装を行うことが多いです。今回もその一例でした。
おわりに
今回は素体の大ざっぱな形状を表現したラティスを自力構築する方針で作ってみましたが、最近はTopo Transfer SOPで素体の対応関係を構築する方法に興味があります。
Houdiniを使用したアバター改変はとても楽しい体験です。KineFXでweightやrigやblend shapeを操作する感覚を覚えるとBlenderでアバターの細かい調整をする気がまるで起きなくなります。
みなさんもプロシージャルアバター改変、やってみませんか。
『背景アーティスト導きの書』詳細目次
榊原 寛・横井 祐子・横井 亮太・橋本 竜・齋藤 彰・岸本ひろゆき・もんしょ
(2023年9月25日刊行、ボーンデジタル、 x+389 pp.、 本体価格4500円、 ISBN:978-4-86246-569-6→版元ページ)
【目次】
1章:制作パイプライン1
はじめに
プリプロダクション
写真資料収集、現地取材
コンセプトアートの作成
小さいテストシーンでアートクオリティベンチマークの確立
各種検証シーン作成
パイプラインの検証と各仕様、 ガイドドキュメントの作成
Vertical Slice
アセットリストの作成
本制作:モデリングとレイアウトから完成まで
まとめ
2章:モデリング・テクスチャリング 61
はじめに
準備
コンセプト.・プランニング
ブロックアウトとモジュール
モデルとテクスチャのプランニング
モデリング
テクスチャリング
マテリアル作成
ライティング
ポリッシュ
LODモデルとコリジョンモデル
まとめ
3章:地形と植生 117
はじめに
地形制作の概要
地形に応じたマスク情報の作成と活用
アセットの自動配置
タイルとLOD
作例
要素分解と事前の設計
ベース地形の立ち上げ
ロケーションの考え方
アセットの自動配置
アセット作成関連
まとめ
4章:レイアウト 167
はじめに
レイアウト作業の流れ
ドラフトレイアウト
アルファレイアウト
ベータレイアウト
ポリッシュ
まとめ
5章:プロシージャル 221
はじめに
プロシージャルについて
プロシージャルのメリット
プロシージャルのデメリット
プロシージャルモデリングについて
プロシージャルモデリングワークフローの設計
プロシージャルビルディングモデリングワークフロー実装
ビル自動生成ワークフローの構成
三分割の実装
特殊等分割の実装
さらなる拡張
作例紹介
まとめ
6章:周辺チームとのコラボレーション 301
はじめに
ゲーム開発チームの全体像
各チームの概要
背景レベルを作成する際のチーム編成と関わり
技術仕様策定・エンジン機能・ツールリクエストの策定時のチーム関係
まとめ
7章:グラフィックス最適化 323
最適化の手順
最適化の注意点
ボトルネックの特定
CPUの最適化
GPUの最適化
テクスチャの最適化
これからの技術
まとめ
メモ:The Library of Hilbert
"The Library of Hilbert"をVRChatにて公開しました。https://t.co/NE2v5fXdoN
— ねむり木 (@Name1ess_Newbie) 2023年1月6日
J.L.ボルヘス「バベルの図書館」とヒルベルト曲線のマッシュアップを試みました。
お越しの際は酔いにご注意ください。#MadeWithVRChat#VRChat#VRChat_world紹介 pic.twitter.com/jR3MGb0gn8
コンセプト
- テーマは配架
- このワールドとNDC Libraryは元々は同じプロジェクト
- 図書館の空間設計を「順序構造をもつ配列を秩序だてて空間に配置する行為」と捉えて、その解決にヒルベルト曲線を利用するというコンセプトだった
きっかけ
- 制作のきっかけは3blue1brownの動画
- 1次元のデータをその構造を維持しつつ空間に格納する手段としてのヒルベルト曲線
- NDCを配架する行為もこれと同じように捉えられるのでは?
その後
- 結局図書館としての実用性に欠けると判断して、NDC図書館とヒルベルト曲線図書館のふたつのワールドに分割
- NDC側が後のNDC Libraryで、ヒルベルト曲線側がThe Library of Hilbert
La biblioteca de Babel
- ボルヘスの「バベルの図書館」を引用している理由は、(NDC側との差別化のために)本の中身がランダム文字列であることを正当化するため
- なのでそこまで本質じゃなかったりする(どちらかといえば演出)
- 言い換えるなら、配架というテーマは変えずにコンセプトを「バベルの図書館に順序構造をいれる」に読み替えた