Bifrostではじめる位置ベース物理シミュ 第1回 ~位置ベース法~

ハイどうもこんにちは！

このシリーズでは、位置ベースの疑似物理シミュや、シンプルなプリミティブのコリジョン検出を利用し、MayaのBifrostでなんやかんや作っていきます。

こんなヤツですね👇

https://twitter.com/akasaki1211/status/1749478630168883557

https://x.com/akasaki1211/status/1749958149698957679?s=46&t=3hUc70jt72Ws5nGlWWELlw  

https://twitter.com/akasaki1211/status/1751408565393539162  

仕組みを理解しようとするとある程度の数学と物理の知識（たぶん高校1〜2年レベル）が必要になりますが、大丈夫です。このシリーズでは一応数式と図を載せはするものの、解説は~~雑に~~簡潔に済ませて出来上がったノードグラフをﾊﾞｼﾊﾞｼ載せていきます。

私自身、リグ歴はそこそこ、物理は趣味でかじった程度、Bifrost歴は執筆開始時点で約2週間というニワカっぷりですので、間違いを含む可能性は十分にあります。おかしな点があれば優しく指摘していただけると助かります。

それでは始めます！

:::note
使用バージョン
- Maya 2024.2
- Bifrost 2.7.1.1

Bifrostグラフそのものの操作説明からは記載していませんので、完全に初心者であればまず公式チュートリアルなどをご確認ください。
🔗[Bifrost ベーシックトレーニング | ムービー | Autodesk :: AREA JAPAN](https://area.autodesk.jp/movie/bifrost-basic-training/)

プラチナゲームズさんのブログもおすすめです。
🔗[Bifröst Blög スタート | プラチナゲームズ公式ブログ](https://www.platinumgames.co.jp/magazine/detail/?id=2_17668)
:::

# 位置ベースの物理シミュレーション

これについては先人達のありがた〜い資料を貼った方が良いので、この場での解説は割愛します。

::: note
参考資料
- [Advanced Character Physics - Thomas Jakobsen](https://www.cs.cmu.edu/afs/cs/academic/class/15462-s13/www/lec_slides/Jakobsen.pdf)
- [Position Based Dynamics](https://matthias-research.github.io/pages/publications/posBasedDyn.pdf)
- [超シンプル物理！～「ドラゴンズ　ドグマ」での衣服／髪シミュの秘密＋剛体／ラグドールへの拡張～（CEDEC 2011）](https://cedil.cesa.or.jp/cedil_sessions/view/603)
- [Position Based Dynamics Omelette コンピュータグラフィックス関連の最新論文紹介（CEDEC 2014）](https://cedil.cesa.or.jp/cedil_sessions/view/1188)
:::

リンクを叩いてすぐそっと閉じた方のために誤解を恐れずに超絶ざっくりまとめると、 **『力（Force）を加えると速度（Velocity）が発生し物体の位置（Position）が変化する』** 力ベースに対し、**『来てほしい場所に直接物体の位置（Position）をいじっちゃおう！』** みたいな手法が位置ベースです。

位置ベースは、**高速、暴れにくい、実装が楽**、などいろいろとメリットがあります。ただし、近似的な手法であるため正しさを求めるようなものではありません。

# 前の位置をもとに今の位置を決める
次の式は“**ベルレ積分**”の式です。「オイオイいきなり積分とか勘弁してくれよ…」という声が聞こえてきた気がしましたが、安心してください。積分と言ってもただの足し算です。

```math
x(t + \Delta t) = 2x(t) - x(t - \Delta t) + \frac{F}{m} \Delta t^2
```

$t$は今の時間、$\Delta t$は1ステップの経過時間です。$x(t+Δt)$ は次の位置、$x(t)$は今の位置、$x(t-Δt)$は前の位置、最後の$\frac{F}{m} \Delta t^2$は追加で加わる力の部分です。$\frac{F}{m} \Delta t^2$は仮に$F$がゼロだったとしていったん無かったことにしてしまいます。$2x(t)$もバラしちゃいましょう。すると途端に簡単になります。

```math
x(t + \Delta t) = x(t) + \{ x(t) - x(t - \Delta t) \}
```

ん？日本語おｋ？

```math
次の位置 = 今の位置 + \{ 今の位置 - 前の位置 \}
```

はい、「今の位置」に「今の位置と前の位置の差」を足しただけ。
**『さっきまでコレくらい動いてたんだから次も同じくらい動くやろ』** 
です。
![fig_01_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/8aeb8fd7-c53c-1eae-ec36-f29b7292ca4e.jpeg)

ここで3つの時間における位置が出てきましたが、次回計算時には次の位置を今の位置、今の位置を前の位置として1つずらして再利用します。

え？話が長い？そろそろBifrostに行きましょうか…

Bifrostグラフで組むと以下のようになります。“再利用”をBifrostグラフ表現するためには **“フィードバックポート”** を使います。これがまぁ便利！グラフ右端の出力がグラフ左端の入力として再利用されます。ちなみにフィードバックポートを使うと値が無限に変化し続けてどこかへ行ってしまうので、現在フレームが0以下だったら規定値でリセットするようにしておきます。

![img_01_01a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/463bb1fa-7a8e-6e12-0406-9cba31497b91.png)
▲next_posをcurrent_posに、current_pos_2をprev_posに、Feedbackします

![img_01_01b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/5dbe4193-1ea9-a23a-9828-b5c9200bd116.png)
▲Next Posの出力だけ何か適当なオブジェクトのTranslateにつないでおきます

再生すると、、、
![gif_01_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/8a6966c7-e0a7-93a9-febe-1682890e098b.gif)

等速直線運動になりましたね！

# 減速させる

先ほどの式は次のように変形すると速度に相当する値を取り出せます。

```math
\begin{cases}
v(t) = \frac{x(t) - x(t - \Delta t)}{ \Delta t} \\ 
x(t + \Delta t) = x(t) + v(t) \Delta t +\frac{F}{m} \Delta t^2
\end{cases}
```

1つ目の式の$v(t)$が速度です。子供の頃に習った気がする【速さ = 距離 ÷ 時間】の形ですね。
また、前の位置$x(t-Δt)$は速度の計算にしか使われていないので、代わりに速度を保存し再利用するよう変更すれば、2つ目の式を使って **『今の位置と速度から次の位置が決まる』** ことになります。前とか今とか次とかややこしかったのが少しスッキリしました。

Bifrostで組むとこのようになります。リセット対象は位置と速度に変わります。$\frac{F}{m} \Delta t^2$は無視したままです。
![img_01_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/55c32ec2-59ed-1803-e703-a349b8b71271.png)
▲30fpsで先ほどと同じ動きになるよう速度の初期値を15にしてあります。これではまだ減速しません。

速度（velocity）が得られたので、減速させるためvelocity値を雑に下げちゃいます。新たにdamping値を作成し、velocityをスケーリングします。dampingが高いほど速度を抑えたいので次のようにします。dampingは0~1の値を入力します。

```math
v(t)' = v(t) * (1 - damping)
```

![fig_01_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/a5301568-4d0a-b8fd-a447-9de75cb12641.jpeg)

Bifrostグラフは次のようになります。
![img_01_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/43690409-780a-5b1a-cb9e-d8941fe479ae.png)
▲追加したのは左側のVelocity dampingの部分です

dampingを0.05にして再生すると、減速して途中で止まるようになりました。空気抵抗を表現している風に見えますがだいぶ大雑把な計算をしているので雰囲気だけです。
![gif_01_03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/f14de0db-b672-b3d6-84d0-6031c432b76b.gif)


第1回でこれしかできていないので先が長そうですが、しょうがないです！次回は今回スルーした$\frac{F}{m} \Delta t^2$の部分に触れていきます。

# 今回のグラフまとめ
※それぞれcompoundにまとめています

全体像（bifrostGraphShape）
![img_01_04a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/b196d61e-0321-7696-ca01-3ca5feb5aedd.png)

次の位置を決定（bifrostGraphShape > Verlet）
![img_01_04b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/b9c5a011-ce80-c73c-43aa-4ed63823733f.png)

速度の更新（bifrostGraphShape > Update_Velocity）
![img_01_04c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/d6d9fa0b-c01c-0386-b7c2-255ee3f175fd.png)

初期値へリセット（bifrostGraphShape > Reset）
![img_01_04d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211093/ea31fa88-9f43-d339-ecdb-b2ba25d73ec4.png)

---

次の記事

https://qiita.com/akasaki1211/items/d7272303d44d4823b39d