Metalによる砂のシミュレーションもどき

MetalのGPGPUによる大量のパーティクル処理能力を活かしたデモ。 パーティクルをそれぞれ砂の一粒に見立てており、上から落下してきた砂粒が山のように積みあがっていく様子をシュミレートしている。

なお、これも約26万パーティクルで60FPSを維持している。

動作イメージ

@m_ike__が投稿した動画 -

(2色にして拡大してみたけど、動画だとイマイチになってしまった・・・)

小ネタ

砂の動き

タイトルに「もどき」とある通り、砂の動きは粒子法などのちゃんとした物理計算をしているわけではないが、GameGems本の中の地形生成の粒子堆積を参考にしている。

フラクタル地形生成-粒子堆積

出典:Game Programming Gems

上図にあるように、

  1. 粒子を地面(粒子)にぶつかるまで落下させる
  2. ぶつかった地点の周囲で最も低い地点を探す
    見つからなければそこに粒子を固定(地面化)させて終了
  3. 2で見つかった地点に粒子を移動させる
  4. 1に戻って落下させ続ける

という動きをGPUで計算している。

コンピュートシェーダのコード

流れとしては、

  1. CPU側で砂の初期位置とy軸への落下速度を設定
  2. コンピュートシェーダで砂の動きを計算して描画用バッファへ書き出し
  3. バーテックスシェーダとフラグメントシェーダはシンプルにパーティクルを描画

となる。

地面との衝突判定

今回は地面との衝突の判定のみで、粒子間の衝突などは考慮しない。よって、512*512のバッファlaminateBufferを地面に見立てて、各座標の高さを記録している。ここでテクスチャを使っていないのは、コンピュートシェーダではテクスチャの読み書きを同時に行うことができない為。

地面にぶつかったら、元の位置からそれぞれ30度+αずつずらした位置の地面の高さを見て、一番低いところを探す。なお、ここで手を抜いてsincosを使わなければ、四角い山ができてしまう・・・

一番低いところの判定を

min_id = mix((float)min_id, 1.f, not(step(h[1], h[min_id])));

としているが、これは

min_id = (h[1] <= h[min_id]) ? min_id : 1;

と同じである。(Metalの座標系のy軸は上が-になる)

現在位置が一番低い場所であれば、y軸の速度を0にして粒子を固定する。同時に地面の高さも更新し、次回参照時からはそこが地面の扱いとなる。

あとはこれをひたすら繰り返していくと山のように積み重なっていく。

感想

パラメータの調整とかバッファへの書き込みの精度を上げたりするなど、まだまだ改良の余地はありそう。。。

なんにしても単純な方法を力任せに処理してもちゃんと動くあたり、GPGPUは頼もしい。

あと、GameGemsの1以外も電子書籍で出ないかなぁ。内容的には古くても色々参考になるし、紙版は置く場所考えるとどうしても買いづらいので

開発環境

ソース

こちら(iOS9 A7以降搭載機種のみ)