
【Quest対応】アバターの顔に海苔が出る症状をModularAvatarで何とかする
イントロダクション
今日はQuest対応の話です。
ここにQuest対応させたラスクちゃんがいます。

シェーダーもQuest対応のものにしたし、PhysBoneの数も減らした。これでそのままQuest版としてアップロードしてもいいけど…アレがまだ解決できてない。
そのアレとは何なのか。表情変化させたときに出てくるこの海苔です。

これって楽な対処方法ないの?って意見をフォロワーから頂いたので、今回はこれをテーマに記事を書いていきます。
御託は良いからとりあえず対処法教えて!あと30分後にイベントなの!って人向けに、解説は後回しにします。
記事も寿司もネタは鮮度が命ですからね。
追記 2024/7/7
なんとこの記事を参考に"NoriBlocker"として商品が出ました。
「取り急ぎ対処したい!」って人はこれ入れればOKです。
対処法
1.ModularAvatarの導入
まずModularAvatarをProjectに導入します。
導入している人はスキップして結構です。

次にプロジェクトを立ち上げます。
今日はこのRUSK-Q100(デフォ衣装ラスク)を弄る想定でやります。

2.作業用フォルダとファイルの作成
まず初めにアセットフォルダ内でファイルを置く場所を決めます。今回はアバターがあるフォルダのAnim(アニメーション関係のフォルダ)に「QuestFacial」というフォルダを作って配置することとします。

自分が分かりやすいところにおいてください
次に作成したフォルダを開きます。まだ何も入っていない状態です。

このフォルダで右クリックして、作成⇒アニメーターコントローラーと作成⇒アニメーションをそれぞれクリックして、アニメーターコントローラーとアニメーションクリップを作成します。

この状態になってればOKです。

次に名前を後から見ても分かりやすいものに変えます。
今回は以下のように名付けました。
AnimatorController - MAFX_Quest_FacialOverride
AnimationClip - Quest_FacialOverride

上がアニメーターコントローラー、下がアニメーションクリップです
次にこの2つを弄っていきます。
3.AnimatorControllerの作成
見出しは長いですがやることは簡単です。
以降はアニメーションコントローラーをコントローラーと呼称します。
まず作成したコントローラーをアバターのAnimatorにある"コントローラー"の枠にドラッグアンドドロップします。

次にアセットフォルダでコントローラーをダブルクリックし、アニメーターの画面を開きます。
この時、アニメーターの下に表示されているパス(画像中央)が正しいか確かめてください。

次に、左上のBase Layerをクリックし、右下のアニメーションクリップを左上のアニメーター画面にドラッグアンドドロップします。

左上のアニメーター画面に、このような形でオレンジの枠(デフォルトステート)が出来ていればOKです。

4.AnimationClipの作成
次にシェイプキーを0にするAnimationClipを作っていきます。
まず画面を以下の通りにしてください。
1.左上をScene画面にする
2.Animation画面を表示する
3.Hierarchyでアバターをクリックする
4.アバターにセットされているコントローラーが正しいことを確認する

次にAnimation画面で、先ほどセットしたAnimationClipの名前になっていることを確認して、録画ボタンを押します。

録画ボタンを押すと、画面のように一部が赤色に変わり、キーフレーム記録モードになります。ついでにアバターが中腰になるのでシーン画面で顔が見える位置に視点を動かしてください。

次に、顔のメッシュをHierarchyで選択し、InspectorでBlendShapes(ブレンドシェイプ)を開きます。

この中で透過テクスチャを使用している表情のシェイプキーを0で記録していきます。今回はcheek(頬染め)からやっていきましょう。

とはいっても、元から0なので、ブレンドシェイプのバーを少し動かして、それから元に戻します。


こうするとAnimation画面で「cheekのシェイプキーを0にする」といった形で記録されます。

これを透過テクスチャを使用しているシェイプキーの数だけ繰り返していきます。やり方としては、
①まずシェイプキーを100にしてみて
②おかしな表示になったら0に戻して記録する
③問題なさそうならシェイプキーを右クリックして「キーを削除する」で元に戻す
ってのが無難だと思います。
次からは画像で説明します。
①まずシェイプキーを100にしてみて

②おかしな表示になったら0で記録

③問題なさそうならキーを削除する



注意点として、ここで問題ないシェイプキーを追加してしまった時は、必ず「キーを削除」で元に戻してください。
一通り追加が終わったら、赤枠の録画ボタンを押して、キーフレーム記録モードを終了します。

ついでにその隣にあるプレビューボタンを押して、プレビューモードを停止させます。こうしないとアバターが中腰のままです。

作業が終わったら、アバターのコントローラーを元に戻しておきます。
Prefabを活用しているのであれば、Animatorのコントローラー枠を右クリックして、「元に戻す」をクリックすれば大丈夫です。

ここまで終わったら、次はModularAvatarを使って、作成したAnimatorをFXに結合させます。
5.ModularAvatarでFXレイヤーを結合する
まずHierarchy上でアバターのルートオブジェクト(先頭になっているオブジェクト)を右クリックして、「空のオブジェクトを作成」をクリックします。

作成したオブジェクトの名前を分かりやすいものに変えます。
今回は"Quest_FacialOverride"にしました。

次に、このオブジェクトにスクリプトを付けます。
「コンポーネントを追加」をクリックし、MA Merge Animatorをクリックして追加します。

次に、追加したMA Merge Animatorの設定を以下の通りにします。
"統合されるアニメーター"の枠にMAFX_Quest_FacialOverrideをドラッグアンドドロップ
パスモードを"絶対的(アバタールートからのパスを使用)"に変更
アバターのWriteDefaults設定に合わせるにチェック

ここまでで追加作業は終わりです。
GestureManagerで動作確認してみましょう。

良さそうですね。
詳細解説(今回何をやったのか)
そもそもアバターの表情は如何にして制御しているのか
まず、アバターにはAvatorDescriptor(アバターディスクリプタ)と呼ばれるスクリプトが付いており、これがないとアバターとしてアップロードできません。
その中の設定項目にPlayable Layers(プレイアブルレイヤー)と呼ばれる設定項目があり、ここにはAnimationControllerをセットすることが出来ます。
移動モーションやハンドサインを制御しているものがありますが、その中の一つがFXレイヤーであり、アバターの表情や着せ替えのアニメーションはこの中で制御しています。

ここで注意してもらいたいのは、「FXレイヤーという名前のファイルがあるのではなく、FXにセットしたAnimationControllerを便宜上"FXレイヤー"と呼んでいる」という点です。BaseやFXはそれぞれ役職のようなものだと思ってください(会社で言うなら部長職は「部長」って名前の人が就いてるわけではないですよね)
レイヤーには処理順がある
FXの中はこんな感じでレイヤー(層)が分かれていて、それぞれのレイヤーの中にはステートと呼ばれる箱があり、それぞれのステートに表情のアニメーションが入っています。VRChatではこの箱同士を行き来することでアバターの表情を変えています。

そしてこのレイヤーの処理順なんですが、下のレイヤーが上のレイヤーを上書きします。
イラスト描いたり画像編集したことある人に滅茶苦茶分かりやすく言うと、画像編集ソフトのレイヤーとは逆で処理されるって覚えておくといいかも。

この辺の詳しい解説はこの記事読んでもらうと分かります。
今回やったこと
話を戻して、ModularAvatarには複数のコンポーネント(部品)があり、その中の一つにMergeAnimator(マージアニメーター)というものがあります。
これが今回使った物で、指定したAnimatorControllerをFXレイヤーの一番下に合体させます。
みんな勘違いしてそうですけど、ModularAvatarって服を着せるためだけのツールじゃないですからね。
つまり元々のFXレイヤーがこの6層だとしたら、

結合後はこうなっています

ここでさっき書いた「アニメーターは下のレイヤーが上のレイヤーを上書きする」というのを思い出してください。
例えば、上の方にあるFacial_LeftHand/RightHandで表情アニメーションを出した結果、shy(照れ)のシェイプキーを100にします。

このままだとshyのシェイプキーが100なので海苔が出てきてしまいますが、今回は一番下にshyのシェイプキーを0にするAnimationClipが入ったレイヤーが控えています。このレイヤーで0を上書きするので海苔が出てこないというわけです。

終わりに
今日はここまでです。
今回やった事は一言でいえば「どんな表情であろうと特定のシェイプキーを特定の数値に固定する」ってテクニックなんですが、勘のいい人ならこの手法を使ってこんなことも出来ます。
メカ改変してるアバターだけ目のハイライトを消す
衣装の特定のパーツを外したときだけ照れ表情で固定させる
Animatorの処理順を理解することで余計なトラブルを回避したり、改変の手間を減らせるので参考にしてください。
では。