ArticulationBodyを使用してMeta Questのハンドトラッキングに物理ベースのインタラクションを追加してみました.まだあんまり安定していないけど,一旦記事にしてみようかなということで ...
ソースコードはこちら
ArticulationBodyを使用してハンドトラッキングに物理ベースのインタラクションを追加したよ.だけどまだあんまり安定してない.
ArticulationBodyはUnityで使用できるJointコンポーネントの1つです.ドキュメントやその他ネットの情報を参考にすると,ロボットアームの実装などで使われることが多く,実際そういう用途を想定したコンポーネントのようですね.
Articulation Bodies enable you to build physics articulations such as robotic arms or kinematic chains with GameObjects that are hierarchically organized. They help you get realistic physics behaviors in the context of simulation for industrial applications. (Articulation Body (連結ボディ) を使うことによって、ゲームオブジェクトを使ったロボットアームやキネマティックチェーンのような 物理連結 を構築することができます。 これらは、産業用アプリケーションのシミュレーションのコンテキストでリアルな物理動作を得るのに役立ちます。)ついでなので,ArticulationBodyの実装の元であるPhysXのArticulationのドキュメントをみるとこんなことがかいてありました.
Articulations provide an alternative, and often superior approach to creating mechanisms versus adding joints to rigid bodies. Typically, you achieve higher simulation fidelity with articulations as they have zero joint error by design, and can handle larger mass ratios between the jointed bodies. PhysX simulates articulations in reduced-coordinates, where the configuration of the articulation is determined by its root body and the joint angles instead of the world pose of each body involved. (関節は、剛体にジョイントを追加するのとは対照的に、メカニズムを作成するための代替の、そして多くの場合より優れたアプローチを提供します。通常、関節は設計上 ジョイント エラーがゼロであり、ジョイントされたボディ間のより大きな質量比を処理できるため、関節を使用するとシミュレーションの忠実度が高くなります。PhysX は、関節を縮小座標でシミュレートします。この場合、関節の構成は、関係する各ボディのワールド ポーズではなく、ルート ボディと関節角度によって決定されます。)この"剛体にジョイントを追加する"っていうのはUnityでいうところのConfigurableJointなどのことでしょうか? PhysXにおける各コンポーネントの実装を全く知らないので適当な感想になってしまいますが,揺れもの以外で物理コンポーネントの連結オブジェクト実装する際には基本的にArticulationBodyを使うのがよさそうな感じななんですかね ...
話は変わりますが,こんな動画を見つけたんです.
すごい ... これはUltraLerpが開発しているUnityPluginのデモの1部ですが,どうやらArticulationBodyを使用して指関節とRigidbodyのインタラクションを実装しているみたいです. ぜひ使ってみたいけど,あいにく自分はUltraLerpを持っていません😢 (VRデバイスはOculus Quest 2しか持っていない !!) ということで今回はOVRで似たもの(廉価版)を実装してみました.
OVRHandPrefabのコピーを作成して,コピーしたほうの手のモデルの各関節にArticulationBodyとCapsuleColliderを追加しています. ArticulationBodyに角度制限を与えて,オリジナルの手のモデルから各関節のローカル空間における回転を毎フレームコピーするっていうシンプルな実装です. 手首 (Wirst) については,フレーム間の位置と回転の差分からvelocityとangulerVelocityを算出してそれを使用する方法をとっています.
今回はOVRHandPrefabで使用されている手のモデルとそのボーンを使用したのですが,なんだか自分の環境ではArticulationBodyのStiffnessとForceLimmitを大きめに設定しないと 軽い物体(500gくらい)ですら掴むのに苦戦してしまいました. 最初,CapsuleColliderはデフォルトのPhysicsMaterialをそのまま使用していたのですが,ArticulationBodyの強さ以外にも コライダーの摩擦を強くするなどの設定が必要そう.実際,UltraLerpのUnityPluginでは手のコライダーに動摩擦係数1のPhysicsMaterialをランタイム生成してArticulationBodyに 使用する処理が存在していたので,最終的にそれを真似しました.
まだまだ不安定です.ArticulationBodyで,フレーム間バグなどでコライダーを貫通してしまった時(それとも異常に強い力が加わったとき ?)などに角度制限に訂正できないおかしなオフセットが追加される問題が発生したりしていました. ただこれについては,どんな状況で発生するものなのかすら正確には把握できていないようなものなので「なんか不安定なところがあるのか~」くらいに受け取ってほしいです. そうえいばUltraLerpのUnityPluginではポジションエラーとローテーションエラーの合計を使用して値が大きくなるほどArticulationBodyで発生させる力を減衰させる処理などもありましたが,あれはこの問題に対処するためだったのでしょうか ... あとはOVRプラグイン側にハンドトラッキングの認識状態の遷移(ハンドトラッキングがロストしたかなど ...)とその状態遷移のイベントコールバックを扱う機能があるかを知らないので,ハンドトラッキングの認識が復活したときに,直前でロストした 位置から現在位置へのArticulationBodyの力の発生となってしまい,ここもうまく制御できていないっていう問題があります(ハンドトラッキング: ロスト ➡ アクティブにフレーム間で遷移したとき,ArticulationBodyを現在位置に移動させるが そこでは力の作用効果をオフにしたい).もう面倒くさくなってきてその辺の実装をしなかったのですが,簡単なやり方だと,そのフレームでの移動量が一定値を超えたらTeleportRootで力の作用効果無しで移動してしまうっていう方法でも十分だったのかな ...