理想のスカート表現を求めて
Clothコンポーネントで実装したVRChatアバター向けのスカートをときどきほめてもらえて嬉しいのでちょっとした解説を書きました。全体的にアバター改変そこそこわかる人向けです。ある程度慣れてないと再現できないかも。
※ 本記事で言及している変更は筆者が勝手に加えたものです。Clothコンポーネントへの置き換えなど本記事の内容に関する質問は元モデルの作者様ではなく筆者 (ろじっく) にお問い合わせください。
やったこと
inugoyaさんのアンティークワンピースのスカート部分の揺れ表現をClothコンポーネントを用いたものに置き換えました。
出来上がったものの挙動は以下のようになっています。
前提知識
エスニヤさんのCloth解説記事、特にConstraints関連の部分を先に読んでおくことをおすすめします。それと、Avatars 3.0 だったりPhysBoneだったりConstraintだったり、Cloth以外のアバター向け要素もいろいろと扱います。また、ところどころメッシュの編集などでBlenderなどのDCCツールも扱うことになります。アレルギーがある方は克服しておいてください。
要件の整理
この手のものは完璧を目指すといつまでたっても終わらないのでやることとやらないことを整理しておきます。今回はだいたい以下のような方針で設計・実装を進めました。
普段よくとる姿勢で破綻しない
まっすぐ立つ
椅子に座る
床に座る: 足を前に出す
床に座る: 足を横に逃がす
通常操作における一時的な破綻は許容する
通常操作: 常識的な速度での移動・姿勢の変化
その後自動で復旧できれば良い
ワープ移動など過激な条件においては自動復旧出来ない破綻も許容する
代わりに手動での復旧手段を用意する
その他
できるだけシルエットを維持する
できるだけテクスチャが不自然に伸びないようにする
基本的なセットアップ
いつものClothスカートセットアップ。ほかにも解説している方がいらっしゃるのでそちらをあたった方がよいかも。
スカート部分のメッシュ分割
Blenderか何かでサクッと分割します。 このとき、境目の部分のウェイトが1つのボーンのみに乗っている状態にしておくといろいろと楽になります。 今回はSpineに境目部分をあわせました。
ウェイトの塗りなおし
スカート部分のウェイトを全て根元のボーン (今回はSpine) のみから影響を受けるように塗りなおします。
コライダーのセットアップ
腰から足にかけてうまくカバーするようにコライダーを入れてください。ここまでのセットアップが完了すると以下の図のようになります。
シルエット・ドレープの維持
パニエで膨らませたようなシルエットとドレープが魅力的な衣装なので揺れをいくら調整してもそこが失われては元も子もありません。しかし、単にClothコンポーネントを入れただけではコライダーと重力の影響しか受けないためストンと下に落ちた形になってしまいます。せめてまっすぐ立っているときはシルエットを維持できるように対策を講じていきましょう。
Surface Penetration によるシルエットの維持
シルエットの維持にあたってはConstraints、特に Surface Penetration が重要となります。詳細はエスニヤさんの解説を参照していただくとして、 Surface Penetration の値を小さくすることで頂点がスカート内側方向に移動できなくなります。今回は Max Distance は 0.3-0.4 の範囲で、Surface Penetration は 0.0-0.01 の範囲で裾に近づくほど大きくなるように設定しています。このあたりのパラメータ (特に Max Distance) はメッシュ形状やスケールなどに左右されるため適宜調整してください。
法線による判定球の制御
このスカートは全体的に波打った形状となっており、単純に法線内側方向に Surface Penetration の判定球を置かれると面の向きが傾いてしまい回転時にスカートのシルエットに沿わない動きの原因となります。
この問題への対策として法線を編集することで判定球の位置を調整してやります。やることは単純で法線をスカートの断面を楕円で近似したときの法線方向と揃えてやるだけです。法線の編集はBlenderであればスカートの形状を近似した楕円錐台から Data Transfer 機能で法線を転送してやると簡単に行うことができます。
リセット機構
何もしなくても破綻しないのならそれが一番よいのですが、VRChatにおいて完全に破綻を回避することは困難でしょう。次善の策として破綻したときに元の状態に復帰するためのギミックを用意しておきます。
Clothリセットの仕組み
Clothコンポーネントを無効化すると対象メッシュの全頂点は元の位置(ボーンによる変形のみの影響を受けた状態)に戻されます。これを利用して、1フレームだけコンポーネントを無効化して次フレームで再度有効化することで頂点位置をリセットすることができます。
コライダーめり込み対策
リセット時に頂点は元の位置に戻されますがコライダーの位置はそのままとなってしまいます。これによってコライダーがメッシュにめり込んでしまった状態となってしまうことがあり、その状態でクロスシミュレーションを再開するとめり込んだ状態からシミュレーションが開始され破綻の原因となってしまいます。
この現象への対策として、リセット直後はコライダーのスカート根本部分のボーンから見た相対位置が初期状態と等しくなるようにしておき、Clothコンポーネントが有効化された後に緩やかに本来の足などに追従した位置に移動させてやります。
コライダーはヒエラルキー上ではSpineに追従した状態として、 Parent Constraint や Rotation Constraint で足など本来の位置に追従できるようにします。こうしておくとConstraintの重みを制御することで初期状態における位置から本来の位置まで滑らかに動かすことができます。
リセット機構の自動起動
物理シミュレーションは基本的に各クライアントで計算されるためいつ破綻するかわかりません。そのため、隙あらば自動的にリセットしてやることを考えます。
コライダーの布へのめりこみの原因の一つとして視界の外 (=シミュレーションが止まっている状態) でコライダーが動いてめり込んだ状態になり、そのままシミュレーションが再開されるケースがあげられます。ここから自動復旧を図るため、視界の外から視界内に復帰したときにリセットアニメーションを再生してやります。
視界外からの復帰の検出はせしおんさんの自動リセット機構をベースにしています。これを応用すると復帰時に任意のアニメーションを再生させるギミックとして利用することができるため、前述のリセット機構を視界外からの復帰時に起動することができます。
Blenderとのやり取りの効率化
自動リセット機構を導入するためにはClothコンポーネントを持つメッシュオブジェクトなどリセット機構によって操作されるオブジェクト群がある程度深い階層に存在している必要があります。何も考えずにBlenderからFBXとしてエクスポートするとルートオブジェクト直下にメッシュオブジェクトが生成されるため、アンパックした後にオブジェクトを移動させることとなります。しかしアンパックしてしまうとその後Blenderで再編集してエクスポートしなおした時の手順が煩雑になってしまうため、アンパックを回避できるようにあらかじめ対策を入れておきます。
具体的な手順としてはBlender上でEmptyを用いて自動リセット機構で使用する階層構造を作ってその中にメッシュオブジェクトを配置しておきます。そして、FBXエクスポート時にEmptyも含めてエクスポートしてやるとArmature以下にEmptyで作った階層構造が再現されます。メッシュオブジェクトもその中に入ったままとなるため、途中にあるEmptyに対してAnimatorなどを追加してやればモデルデータをアンパックすることなく作業を進めることができます。
Expression Parameter による制御
自動リセット機構の中の "Resetter" のAnimatorを1フレームだけ無効化するようなアニメーションおよびステートマシンを組んで、それをFXレイヤーで再生すると自動リセットと同じ処理を走らせることができます。これによって Expressions Menu や他のギミックをトリガーとしてもリセット機構を動作させることができるようになります。
着座時の挙動の改善
ここまでの状態だと着座時に膝あたりの布が伸びやすい状態となってしまいます。また、かがんだ時にも前の方が引っ張られてしまい場合によっては貫通の原因となってしまいます。さらに、立位前屈のような体勢をとった場合に後ろ側の布が重力に従って真下に落ちずに腰の方までずり上がってしまう問題もあります。
コンセプト
これらの問題の根源は Surface Penetration 制約によって後ろ側の布が前に移動することができず、コライダーによる前方向への引っ張りの吸収に利用できなくなってしまっていることにあります。
対策として、腰を下ろしたときに Surface Penetration の判定に用いる球を前方向にずらすことを考えます。判定球の位置は常にボーンによる変形の影響を受けるため、判定球の制御のためのボーンを入れてそれを腰の高さに連動させて動かすことで立位時のシルエットと着座時の頂点移動の自由度を両立させることができます。
補助ボーンの導入
よくあるPhysBoneベースのスカートのようにボーンをいれてやります。今回は作業の流れの都合で一度元のボーンを消してしまっていたため手動で入れなおしていますが、元々入っているものを流用してもよいかもしれません。
補助ボーンの制御
まず、腰の高さをボーンの回転に変換する機構を用意します。これは以下の図のようにアバター直下にある平面コライダーと腰から下方向に伸びたPhysBoneで作ることができ、腰の位置が下がると前方向に回転するTransformが得られます。
次に、ここで得られた回転をスカートに対応する補助ボーンに適用してやります。これは Rotation Constraint で実現することができ、スカートの各ボーンが上述の手順で得られたTransformの影響を受けるようにしてやると良いです。このとき、ボーンごとに重みを調整して体の輪郭から離れすぎないようにしてやりましょう。
前屈対策
腰の高さを検出するためのPhysBoneが重力の影響を受けるようにしてやると前屈時にスカートの補助ボーンがそれを打ち消すように動いてくれます。これによって下図左のような前屈時の破綻を下図右のように回避することができます。
リセット機構との併用
Clothコンポーネントはそれが有効になったタイミングでの頂点間の距離を使用してシミュレーションを行います。この時の頂点間の距離はボーンによる変形の影響を受けるため、リセットの際にはここで使用した補助ボーンも初期位置に戻す必要があることに注意してください。
掴めるスカート
掴めるスカート、いいですよね。PhysBoneの登場でそこまで珍しい表現でもなくなりましたがClothベースでも掴めるようにしましょう。よく見ると布のある一点を掴むのとは少し違う挙動だったりしますがまあそれっぽい動きということで。
基本的な仕組み
基本的な原理は簡単で、手に追従するコライダーを新たに用意してやるだけです。他プレイヤーが触れるようにする必要がないのであればハンドサインなどのアニメーションで手の近くにコライダーを出してやるのが楽で良いでしょう。ただし、コライダーの有効無効の制御は少し注意が必要です。Clothコンポーネントはコライダーコンポーネントの有効無効を考慮しないため、無効化する代わりにサイズを極端に小さくして頂点と頂点の間をすり抜けられるようにしてやります。
他プレイヤーからの干渉
他の人からも触れられるようにする場合、PhysBoneの先端にCloth用のコライダーをつけてやります。今回は下図のようにUpperLegに重なるように掴み判定用のPhysBoneを配置しています。コライダーを動かせる範囲はボーンの長さとPhysBoneのStretchで調整してやります。また、PhysBoneではボーンが掴まれているかどうかをAnimatorのパラメータとして提供しています。それを利用して掴まれていないときはコライダーを小さくしておくと破綻の原因をひとつ減らすことができます。
掴みコライダーの位置調整
このままだとコライダーの中心が手のあたりに来るため、掴んだ時に手がスカートの中に埋まってしまいます。ちょっと不格好なのでコライダーがその半径分くらいスカートの内側に来るように調整してやりましょう。
コライダーの位置調整 (内側へのオフセット) はConstraintの組み合わせで行います。PhysBoneの先端要素をGrabbable.Lとすると以下のように組んでやります。
GrabbableColliders: アバター直下に配置。Position Constraint で Spine の少し上あたりに来るように調整。
Aim.L: Z+がGrabbable.Lの方向を向くようにする Aim Constraint。Up Vector はY+。
Z: Aim.L上に射影されたGrabbable.Lの位置。向きはAim.Lと同じ。Position Constraint の Freeze Axis を使う。
Y: ZのY-方向がスカート内側を向いているため、その方向にTransformでオフセット。
Collider.L: Position Constraint でYに追従。
積み残し
できそうな気はするけどまだできていないこととか。揺れ物の道は長く険しい……。
補助ボーンを腰の高さではなくUpperLegの曲がり具合で制御する
Contactsによるワープ検出
フレームレートによる挙動変化の吸収
変形後のメッシュ形状からドレープによる影を求めなおす
おわりに
PhysBoneのままでもかわいいので少しでもアンティークワンピースが気になったらぜひ導入してみてください。
謝辞
調整中ラケシアさんには度々相談に乗っていただきました。改めてお礼申し上げます。
この記事が気に入ったらサポートをしてみませんか?