- 1. 参考書籍
- 2. Boidsアルゴリズムとは
- 1. 分離(Separation)
- 2. 整列(Alignment)
- 3. 結合(Cohesion)
- 4. 最終的な位置の計算
- 5. Boids計算の注意点
- 3. Boidsシミュレーションの実装
- 1. GPUインスタンシングとComputeShader
- 1. ComputeShader
- 2. GPUインスタンシング
- 2. GPUインスタンシングによる描画
- 1. GPUインスタンシングに対応したシェーダーを書く
- 2. マテリアル上で有効にする
- 3. RenderMeshIndirectで描画する
- 3. 群集シミュレーションの実装
- 4. 自作の魚オブジェクトの設定
- 4. おわりに

マイケル
みなさんこんにちは!
マイケルです!

エレキベア
こんにちクマ〜〜〜

マイケル
今回は久しぶりのUnityです!
下記のような魚の群集シミュレーションを作ってみました!

▲魚の群れができている

▲シミュレーション空間

エレキベア
おお〜〜〜
なんかすごいクマ〜〜〜〜

マイケル
これはBoidsアルゴリズムという有名な手法でシミュレーションしたものなんだ!
実装は下記のUnityGraphicsProgrammingを参考に実装しました!

UnityGraphicsProgramming vol.1

エレキベア
UnityGraphicsProgrammingは無料公開されていて
情報も膨大だからほんとにありがたいクマね

マイケル
今回実装したプロジェクトは下記リポジトリにもあげています!
こちらも合わせてご参照ください!
GitHub - unity-boids-simulation
▲サンプルプロジェクトのリポジトリ
- Unityバージョン
- 2022.3.16f

エレキベア
解説楽しみクマ〜〜〜
参考書籍

マイケル
Boidsアルゴリズムはグラフィックス分野の他、ゲームAI分野でもよく取り上げられています。
UnityGraphicsProgrammingの他に、下記書籍も参考にさせていただきました!

Unreal Engine 5で学ぶビジュアルエフェクト実装 基本機能からNia...


エレキベア
定番のアルゴリズムなのクマね

マイケル
また、今回ComputeShaderなどHLSLを使用したシェーダーも実装します。
HLSLについては下記書籍が体系的にまとまっていておすすめです!

HLSL シェーダーの魔導書 シェーディングの基礎からレイトレーシングまで

エレキベア
これもいい書籍だったクマね
Boidsアルゴリズムとは

マイケル
まずBoidsアルゴリズムとは何かというと、
1987年にSIGGRAPHにて発表された群集シミュレーションアルゴリズムです。

エレキベア
英語でよく分からんクマ...

マイケル
内容について簡単に説明すると、
分離(Separation)、整列(Alignment)、結合(Cohesion)という3つの規則により群れのような動きを再現するアルゴリズムになっています。
これらの規則により加える力を合わせて操舵力とも消されます。
Boidsアルゴリズムとは
- 1987年に発表された群集シミュレーションアルゴリズム
- 分離(Separation)、整列(Alignment)、結合(Cohesion)という3つのルールにより群れのような動きを再現する
- これらのルールにより加える力を合わせて操舵力とも呼ぶ

▲Boidsアルゴリズムの規則(分離、整列、結合)

エレキベア
ほうほう・・・
聞いた感じだとシンプルクマね

マイケル
それではこの分離、整列、結合のそれぞれのルールについて内容を説明します。
分離(Separation)

マイケル
1つ目の分離は、ある一定の距離内にある個体と密集することを避けるように動く規則です。

エレキベア
近づきすぎたら離れるということクマね

▲近づきすぎたら離れる

マイケル
これをコードで書くと下記のようなイメージになります。
各個体との距離を調べて、一定距離以下の個体との位置差分の平均を力として計算しています。
▲分離の判定

エレキベア
簡単な計算クマね
整列(Alignment)

マイケル
2つ目の結合は、ある一定の距離内にある個体が向いている方向の平気に向かおうと動く規則です。

エレキベア
みんなで同じ方向に向かうのクマね

▲同じ方向に向かう

マイケル
コードのイメージは下記のようになります。
分離の時と似ていますが、平均を取るのは位置ではなく各個体の速度となっています。
▲整列の判定

エレキベア
どっちも同じような計算クマね
結合(Cohesion)

マイケル
最後に整列は、ある一定の距離内にある個体の平均位置に動く規則です。

エレキベア
分値と違うのは複数個体の平均位置に動くということクマか・・・

▲複数個体の平均位置に動く

マイケル
コードで書くと下記のようなイメージになります。
平均位置を求めて、自身の位置との差分を力として加えています。
▲結合の判定

エレキベア
これで3つの力が計算できたクマ〜〜
最終的な位置の計算

マイケル
計算した力を操舵力として位置の計算を行います。
こちらの処理は下記のようになっていて、合わせてシミュレーション範囲からはみ出さないよう境界処理も挟んでいます。
▲最終的な位置の計算

エレキベア
これでシミュレーションの流れが分かったクマね
Boids計算の注意点

マイケル
計算方法自体はシンプルですが、個体ごとに他の個体との判定処理を行う必要があるため、大量オブジェクトでシミュレーションする場合の計算不可が高いという注意点があります。

エレキベア
単純に考えてN^2の計算量になるクマね・・・

マイケル
空間分割等で計算量を抑えることもできますがそれでも計算が多いことには代わりないため、GPUによる並列計算を行う場合が多いです。
今回は
シミュレーション計算:ComputeShader
オブジェクト描画:GPUインスタンシング
の手法をそれぞれ使用しています。

エレキベア
これからUnityでその実装を行なっていくクマね
Boidsシミュレーションの実装

マイケル
それではBoidsアルゴリズムを実現するためにUnityで実装していきます。
先ほど記載した通り、
シミュレーション計算:ComputeShader
オブジェクト描画:GPUインスタンシング
の手法をそれぞれ使用します。
GPUインスタンシングとComputeShader
ComputeShader

マイケル
ComputeShaderは、描画処理以外の汎用的な計算をGPUに行わせるためのものです。
GPUに計算させることで並列計算が高速に行えるので、今回のような大量オブジェクトの計算に非常に向いています。
ComputeShaderとは
- 描画処理以外の汎用的な計算をGPUに行わせるためのもの(GPGPU)

マイケル
ComputeShaderの詳細やUnityでの使い方については下記記事でも紹介していますので、
こちらもよければご参照ください!

【Unity】ComputeShaderの基本的な使い方についてまとめる
2023-01-07

【書籍紹介】「ゲーム制作者になるための3Dグラフィックス技術」に出てくる用語を簡潔にまとめる
2022-12-15

エレキベア
ComputeShaderは並列計算による効率化が測れるクマね
GPUインスタンシング

マイケル
GPUインスタンシングとは不要なGameObjectの生成を行うことなく、同じメッシュのコピーを一度に描画できる手法のことです。
その結果、ドローコールの数が軽減され、大量オブジェクトを高速に描画できるようになります。
参考:
Unity Documentation - GPUインスタンシング
GPUインスタンシングとは
- 不要なGameObjectの生成を行うことなく、同じメッシュのコピーを一度に描画できる手法
- 使用されるドローコールの数を減らすことができる

エレキベア
GPUに直接描画命令を出せるクマね
GameObjectの機能を使わないのであればその方がよさそうクマ

マイケル
GPUインスタンシングは下記手順で使用することができます。
- GPUインスタンシングに対応したシェーダーを書く
- マテリアル上で有効にする
- RenderMesh(DrawMesh)系のAPIを介して描画する

マイケル
RenderMesh(DrawMesh)系のAPIについては何種類もありすぎてどれを使えばいいのか分かりづらいのですが、
大体下記のような区分になっていると思います。
参考:
Unity Documentation - Graphics
RenderMesh関連のAPIについて
Unity2021未満 | Unity2022以降 | |
---|---|---|
単一メッシュの描画 | DrawMesh | RenderMesh |
複数メッシュの描画 (一度に1023個まで) | DrawMeshInstanced | RenderMeshInstanced |
複数メッシュの描画 (ComputeShaderサポート、引数バッファ渡し) | DrawMeshInstancedIndirect | RenderMeshIndirect |
複数メッシュの描画 (ComputeShaderサポート、インスタンス数直接指定) | DrawMeshInstancedProcedural | RenderMeshPrimitives |

エレキベア
複数メッシュ描画の中でもComputeShaderを使用するものとそうでないものがあるクマね

マイケル
Unity公式の下記動画でも紹介されている通り、Unity2022以降は「DrawMeshXXX」から「RenderMeshXXX」という名称に変更されたらしく、基本的にあそちらを使用するようにした方がよさそうです。

マイケル
今回はComputeShaderを使用する前提のため、「RenderMeshIndirect」関数を使用する方向で進めました。

エレキベア
これで実装方針は決まったクマね
GPUインスタンシングによる描画

マイケル
それでは早速実装の紹介に入っていきます。
まずはGPUインスタンシングによる描画の部分からです。
GPUインスタンシングに対応したシェーダーを書く

マイケル
まずはGPUインスタンシングの対応したシェーダーを書く必要があるのですが、今回はSurfaceShaderを使用しているため記述も最小限となっています。

マイケル
対応が必要な箇所としては
・instancing_optionsでprocedural関数の指定を行う
・「unity_InstanceID」(各個体ごとのID)を使用して頂点座標の計算を行う
・「UNITY_PROCEDURAL_INSTANCING_ENABLED」(RenderMeshIndirectで呼び出す時に有効になるdefine)で頂点座標の計算部分を囲む
の3点です。
Unity Documentation - Graphics.RenderMeshIndirect
▲受け取った情報から頂点座標を計算して描画する

エレキベア
「_BoidDataBuffer」が受け取る計算データで、それを「unity_InstanceID」から該当のデータを取得して計算しているクマね
マテリアル上で有効にする

マイケル
そしてこのシェーダーを設定したマテリアルの「Enable GPU Instancing」フラグをONにすることでGPUインスタンシングが有効になります。

▲GPUインスタンシングを有効にする

エレキベア
まさかそんなところにフラグがあったとは・・・
RenderMeshIndirectで描画する

マイケル
あとはC#側でRenderMeshIndirect関数を使用して描画処理を実装すれば完了です!
こちらはGraphicsBufferで生成したバッファと、RenderParamsにデータを設定して渡すことで描画することができます。
▲GPUインスタンシングのための引数バッファを定義
▲RenderMeshIndirect関数で描画する(シミュレーションデータはこれから計算する)

エレキベア
確かGraphicsBufferはComputeBufferの後継的な立ち位置だったクマね
必要なデータの設定方法はリファレンスやサンプルを見るしかなさそうクマ
群集シミュレーションの実装

マイケル
あとはシミュレーション計算を行えば群集シミュレーションの実装は完了です。
こちらはComputeShaderで下記のように実装しています。
冒頭で紹介した分離・整列・結合の判定を行い、操舵力として計算しています。
▲操舵力の計算処理

マイケル
計算内容は冒頭で紹介したものと同じですが、計算を効率化するためにスレッドグループ共有メモリを使用しています。
こちらはスレッドグループ内で共有・高速アクセスできるメモリで、データを一括で格納した後に参照することで効率化することができます。
参考:
【Unity】実践!Compute Shaderを最適化してみよう - Zenn

エレキベア
GPUの仕組みを活用した処理クマね

マイケル
計算した操舵力は下記のように別カーネルで速度に適用して位置を更新しています。
▲操舵力を位置に適用

エレキベア
シミュレーション範囲に収まるように制御も加えているのだったクマね

マイケル
最後に、下記のように必要な情報を設定してカーネルを呼び出すようにすれば実装は完了です!
▲必要なデータを設定してカーネルを呼び出す
▲シミュレーションに必要なデータの定義

エレキベア
やったクマ〜〜〜〜
自作の魚オブジェクトの設定

マイケル
仕上げとして、自分で作成した魚モデルを群集として泳がせてみます。
今回はBlenderにて、下記のようなローポリゴンなモデルを作成してみました。

▲Blenderで制作した魚

▲頂点数は50ほど

エレキベア
落書き感がすごい・・・

マイケル
こちらのモデルを設定して実行すると・・・
下記のように美しい群を作りながら泳ぐことを確認できました!

▲いい泳ぎっぷりである

エレキベア
おお〜〜〜
感動クマ〜〜〜〜
おわりに

マイケル
というわけで今回はUnityでの群集シミュレーション実装でした!
どうだったかな??

エレキベア
簡単な規則なのにそれらしい群れが出来て感動したクマ〜〜

マイケル
ゲーム内で使用するにはもう少し効率化の面で工夫が必要そうだけど、演出として使用するには十分使えそうだね!
Unityで実装した大量オブジェクトの描画手法も、今後活用できそうです。

エレキベア
外部の環境にインタラクティブに反応する実装もやってみたいクマね

マイケル
それでは今回はこの辺で!
アデューーー!!

エレキベア
クマ〜〜〜〜
【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する 〜完〜