技術記事です。GitHubにレポジトリを置いたので、それの紹介みたいな感じです。
- やりたいこと
- 先行事例
- 作ったもの、ポイント
- やってないこと
1. やりたいこと
「動的にロードしたVRMアバターの髪を揺らしたい」というのがモチベーションです。
拙作「VMagicMirror」の最新版(v0.9.3)で実際に動かしたのがコチラ。
#VMagicMirror
— 獏星(ばくすたー) / Megumi Baxter (@baku_dreameater) 2019年11月4日
風の有無を比較するとこんな感じです。
こっちの比較では差を強調するため顔トラッキングをオフにしており、「髪が揺れてた方が絵面がもたせやすくて良い」みたいな様子が見て取れます pic.twitter.com/oXrkaZspP5
ツイート中でも言及していますが、そもそもはAniCast Makerの取り組みを参考にして作っています。13:20あたりが該当箇所なので、あわせてご覧下さい。
2. 先行事例
VRMSpringBone
のcenter
を使った方法が試されています。
https://lasmi.booth.pm/items/1654767lasmi.booth.pm
これも良いんですが、個人的にはちょっと設定がトリッキーに感じて採用を見送りました。
3. 作ったもの、ポイント
GitHubはコチラです。
導入手順等についてはGitHubのほうを見て下さい。
こちらでは計算アプローチを紹介しますが、発想は非常にストレートフォワードです。
- 風が吹くことを力の一種として表現することにする
- キャラが持っている`VRMSpringBone‘を拾い集める
- 集めた
VRMSpringBone
のそれぞれについて、重力項のm_gravityDir
とm_GravityPower
から求まる力ベクトルに、風のベクトルを後乗せした値を書き込む - 風の強さを時間に応じて強弱させて、風っぽさを増す
コツは「最初から入っている重力+風で作った力」というのを常に与え続けることです。
//windItem: 時間に応じて風の向きと強さを計算している別の要素 Vector3 windForce = Vector3.zero; for (int i = 0; i < _windItems.Count; i++) { windForce += _windItems[i].CurrentFactor * _windItems[i].Orientation; } for (int i = 0; i < _springBones.Length; i++) { var bone = _springBones[i]; //NOTE: 力を合成して斜めに力をかけるのが狙い var forceSum = _originalGravityFactors[i] * _originalGravityDirections[i] + windForce; bone.m_gravityDir = forceSum.normalized; bone.m_gravityPower = forceSum.magnitude; }
これも重力を曲げている点でたいがい胡散臭いんですが、それでも力として扱えると気がラクなので、私は好きです。
4. やってないこと
今回の実装ではVRMSpringBoneを無差別に拾うようになっています。
そのため、以下のような挙動は実現できません。
- 髪だけ揺らしたい、スカートと胸は揺れて欲しくない
- 髪とスカートを別方向に揺らしたい
これらに対応するためには、各VRMSpringBone
がアタッチされたボーンがHumanoidBones
のどのボーンの子なのか調べるのが良いと思います。
例えばHead
の子なら髪であると推定できますし、Hips
付近の子ならばスカートの可能性が高いです。
この辺は大いに改善の余地があるので、各人の都合でいい感じにしてもらえればと思います。