見出し画像

VRChatで使うアバターをHoudini(kineFX)でセットアップする

Houdini Apprentice Advent Calendar 2022 4日目の記事になります!

はじめに

yoyogi moriのHATRAのアバターを見て感化されたのもありつつ、Houdiniでアバターのセットアップって出来ないのかなと奮闘した内容になります。
色々と調べた結果、細かいところで躓いたものの思ったよりスムーズにできたのでHoudiniって便利だな~と改めて思いました。

注意点

Houdiniのバージョンは「19.5.403」で作成しています。
基本的にベースとなるキャラクターモデルを改変していく形になるのでゼロから作る内容ではないです。
また「Houdini Apprentice Advent Calendar」の記事ではあるんですが、FBXの出力はindieライセンス以上じゃないと出来なかったはずなのでその辺はすみません……。

kineFXとは

Houdini18.5から追加されたキャラクターリギングまわりのノード群のことです。19.0や19.5でも追加されたノードがいくつかあり、比較的新しいノードなのでドキュメントも日本語化されていないものもあったりで少し戸惑いました。またブレンドシェイプの扱いが少し独特だったり、FBXの仕様(Houdiniの仕様?)なのかよくわからない挙動もあったりしましたが、一応解決できたのでその辺も書いていきます。

公式で提供しているチュートリアルも多いのでその辺は親切ですね。下記の動画一式を見ればある程度は使えるようになるんじゃないかと思います。


基本のノード

今回は「くろなつ」をベースに改変します。

FBX Character import から初めて諸々のノードを繋いでいる形です。このノードには3つのアウトプットがありますが、左から「メッシュ情報」「ボーン情報」「アニメーション情報」が格納されています。
メッシュ情報には初期状態だと中途半端にマテリアルが適用されていたりするので作業しやすいようにAttribute Deleteで不要な情報を削除しています。

Character Blend Shape Channelsでブレンドシェイプがいじれたり、Rig Poseでボーンを動かすことでキャラクターが動かせます。Rig PoseはSlelton Blendをかませることで複数のRig Poseを混ぜたり使い分けしたりできるので便利です。Houdini内部で使う分にはこれで十分ですね。

ウェイト情報を調整する

「自分のアバター衣装を変える」というのが一番やりたいことだと思うので、そのためにボーンのウェイト調整をします。
これに関しては色々と方法があると思いますが、今回は「ボディのウェイト情報を転写する」と「メッシュに元からあるウェイト情報を利用する」の2つを説明したいと思います。

ボディのウェイト情報を転写する

ボディのウェイト情報をメッシュに移動させる方法は改変時の定番的な方法かと思います。
今回はVellumで作成した簡単な服を例にセットアップします(服の作り方は本筋から外れるので省略)。

注意点としてはVellumのCollisionとしてFBXメッシュを使う場合はCharacter Blend Shapesノードを通さないとブレンドシェイプが全て効いた状態でシミュレーションに渡されて妙なことになります、なりました…

ウェイト情報の転写はメチャメチャ簡単で、ウェイト元のボディメッシュのpointアトリビュートにある「boneCapture」一式を服のメッシュに移動させればOKで、Attribute Transferを使えば一発です。

Object Mergeノードで服のデータを読み込んでいます

もう少し説明するために改めてFBXメッシュをスプレッドシートで見ると、pointアトリビュートに「boneCapture」という項目が一杯あると思います。これがウェイト情報で、ボーンの名前とウェイトのかかり具合がpointごとに格納されています。これをそのまま服のメッシュにも移動させることでボディと連動して動く形になります。
このようにノード一つで転写可能ですし、仮にこの状態で服のモデルを修正したとしても自動でウェイトを転写してくれます。便利ですね。

pointに紐づくボーン情報が格納されているのが肝で、この後で説明する「メッシュに元からあるウェイト情報を利用する」で別の使い方をします

とはいえ、単純に転写しただけだとウェイトが不格好だったりするので手動で調整しましょう。Joint Capture Paintノードを使います。

Joint Capture Paintノードで他ソフトでもあるようなウェイトペイントができます。通常操作でペイントしつつキーボードの「A」でボーン選択モードになったりと使い勝手も良いと思いました。
注意点としては手動でウェイト編集をするとプロシージャルなデータじゃなくなるので、例えばこの作業の後に再度服を編集してトポロジーが変わるとエラーを吐きます。その場合は面倒ですが再度ウェイトを調整してください。
あと、このノードを使っているとHoudiniがめちゃくちゃ不安定になって頻繁に落ちます……こまめに保存しましょう。
どうしても安定しない場合はCapture Layer Paintという似たような機能のノードがあるので、そちらでペイントするのも手です(似たようなノードが2つある理由がわかっていない)。

転写については以上です。意外と少ないノードで実現できたのではないでしょうか。

メッシュに元からあるウェイト情報を利用する

アバター改変の際は別のモデルで使われている衣装を自分のアバターに着せたい場合もあると思います。その場合、元のアバターで設定されたウェイト情報がメッシュに残っているので、それをそのまま現在のアバターでも利用したいと思います。
今回は「トラスとウェッジ」のジャケットを自分のアバターに着させたいと思います。

手っ取り早くマージしてみた図

先程のアバターと同じくFBX Character importでデータを読み込み、サイズ調整や余分なデータを削除した上で自分のアバターメッシュとマージした状態です。
この状態でボーンを動かすと、特にウェイトを調整してないのに手首部分だけ追従して動きます。
この理由としては、先程のスプレッドシートを思い出して欲しいのですが、pointアトリビュートに対応するボーンの名前とウェイト情報が格納されていたところにヒントがあります。
つまり、ジャケットメッシュに格納されているボーンの名前と自分のアバターのボーンの名前が同じであればそのままウェイト値を参照して連動してくれるということになります(手首が動いているのはボーン名がたまたま同じだったからです)。
左腕まわりのボーンの名前をスプレッドシートでよく見ると、自分のアバターのボーンが「Upper Arm.L」なのに対してジャケットのボーン情報が「UpperArm.L」になっていて、半角スペースの有無で微妙に情報が異なっていました。ここを統一させれば上手く動きそうですね。

VRChat用に作られたアバターはUnityのHumanoidを意識したボーン構造になっているのでこの辺の名称も近しいものになってます

Attribute String Editノードを使ってジャケットメッシュのアトリビュート内の文字列を置換していきます。
今回のジャケットメッシュは肩のウェイトを2箇所のボーン(UpperArm.L と UpperArm.Twist.L)で制御していたので、とりあえず簡易的な処置として一つ(Upper Arm.L)にまとめています。

これでもう一回ボーンを動かすと追従するようになります。もともとの制作者が調整したウェイトなので自分でラフに調整するよりも綺麗ですし、ウェイトペイントの調整は中々に骨が折れる作業なので非常に助かります(もちろん先程と同様にjoint capture paintを使えばここから更にウェイトペイントの調整もできます)。
メッシュにウェイト情報がある場合はボディメッシュから転写するよりもこちらの方がメリットが大きいですね!

最終的なノード

ボディメッシュと服のメッシュしかなかったので髪などのメッシュを再度マージして、その後にMaterialノードでマテリアルの設定をします。今回はHoudini内部ではレンダリングしない想定なので、マテリアル分けはしますが内容は全部デフォルト設定の状態です。

このままでもFBX書き出しはできるのですが、最後に「fbx_material_name」というアトリビュートをメッシュへ追加します。これを追加しないと他ソフトでFBXを読み込んだ際にマテリアル名が空欄になります。

何か右側にもう一個ノードがありますが、これについては記事の一番最後にAppendixとして追記してます

設定したマテリアルの名前をそのまま使いたいので、Akira Saito先生がTwitterで紹介していた下記のVEXをFBX Character Outputの前に入れます。

string path[] = split(s@shop_materialpath, "/");
s@fbx_material_name = path[len(path) - 1];

これで今回は終了です!
これくらいであれば少ないノードで実現できて、かつプロシージャルなのでモデルを入れ替えても対応できるので大変良いのではないでしょうか。
もちろん制作者ごとにメッシュやボーンの命名規則が違ったりと情報の差異があるので全くの同じノードでは上手くいかない場合もあるとは思いますが、最低限の修正で使いまわせそうな気がしています。

今回のジャケットメッシュには揺らし用のボーンが別途仕込んであったので、そのボーンと自分のアバターのボーンを繋げて再利用することも出来たのですが、想定より記事が長くなってしまったのでこの辺で切り上げようと思います。
また機会があればまとめます、今回はこの辺でさようなら!!!!!Houdini最高!!!!!!!!!!!!!

楽しい Houdini & VR ライフを!

Appendix: 超簡易的なkineFXセットアップ

ただのBoxにボーンとブレンドシェイプを設定してFBX書き出しできる状態のデータです。サンプルデータを用意したので詳しくは中を見てもらった方が早いと思います。

アバターセットアップ説明時にあまり触れられなかったのですが、ブレンドシェイプの設定もこんな感じでできます。が、何か独特というかクセがありますね……。
ブレンドシェイプ用に変形したメッシュをPacked PrimitiveとしてまとめてCharacter Blend Shapes Addノードの2つ目のインプットへ入れることでブレンドシェイプが設定できます。

primitivesの一番下にブレンドシェイプが追加されている

この状態でFBX Character Outputで出力するとブレンドシェイプとして書き出されますが、何とも不思議な気分。
下記の記事でもっと詳しく説明されていましたが、このときは18.5だったのでAttribute Wrangleで設定したようです。19.0でCharacter Blend Shapes Addが追加されたのでこの時よりかは楽になってはいますが……。

Appendix: FBX書き出しの際にボーンのスケールがおかしくなる

Houdiniの仕様なのかバグなのか自分が勘違いしているだけなのかわからないのですが、FBX Character Outputでデータを書き出すと親ボーンのスケールが100になります。

書き出したFBXをUnityで見ると親ボーンのスケールが100になっている

恐らくFBXのユニットスケールの問題で、書き出しの際に値を100倍しているのが原因だとは思うのですが、メッシュは正しく1になってるしボーンも親だけ100で子は1だしでどうしたらいいんだと3日くらい悩んでしまった……。

再度ドキュメントを見ていたところ、Houdini内部のボーン情報はxyzの座標に加えて「transform」というアトリビュートでそれぞれのボーンの角度やスケールを管理しているようです。なので、書き出し前のボーンのtransformアトリビュートに対してスケールを0.01してやることで問題が解決できました。transformアトリビュートは3x3の行列になっているので、スケールも行列形式で計算する必要があります。

matrix3 ident = maketransform({0, 0, 1}, {0, 1, 0});
float amount = 0.01;
scale(ident, amount);

3@transform *= ident;

なぜこんな仕様になっているのでしょうか……知見が浅く誰かご存知であれば教えて欲しいです……。

この記事が気に入ったらサポートをしてみませんか?