こんにちは!お久しぶりですーどうもあしかぺんぎんです。
3連休の最終日ですがいかがお過ごしでしょうか。
私はUnityでのプログラミングがうまく行かず悩みに悩んだ3連休(実は先週の3連休も)でした。
しかーし!とりあえず納得行く結果ができましたので、紹介したいと思います。
今回はUnityの流体計算です!以前に無料のAsset「NVIDIA Flex for Unity」を用いて流体計算を実施しましたが、今回は自分自身で計算してそれを描画表示させることに挑戦しました。
流体計算には「LBM(格子ボルツマン法)」を用いました。それでは行きましょう!
がんばったんだよ~
格子ボルツマン法とは?
流体計算いろいろ
格子ボルツマン法はLBM(Lattice Boltzman Method)と呼ばれる流体計算の手法です。
流体計算と言っても難しいと思いますが、つまりは水の流れを計算するわけです。
水は高いところから低いところへ流れますし、壁があると跳ね返ったりします。
この水の性質を記述した方程式にナビエ・ストークス方程式というものがあります(以下の式)。
なんじゃこりゃ!頭の悪い私にはなにかの暗号にしか見えませんが、どうやらこれで水の流れが計算できるみたいです。
といってもこの方程式は一般解(x=○○!みたいなやつ)がまだ求められておらず、ミレニアム懸賞問題に指定されていまして、解くと100万ドルがもらえるみたいです!
まあ私には関係ない話ですね~(鼻ほじ~😃)。とても難しい方程式なのでコンピュータを使って近似して計算するのが一般的です。
とはいえ真面目にコンピュータで計算しようとすると膨大な計算時間とメモリが必要となります。
私が研究者で「流体の挙動をきめ細やかに調べる必要がある!」とかだったらスパコンなんかを使って大量のメモリを消費して計算するのでしょうが、ゲームのエフェクト等に使うのが目的ですので、まあそれっぽい水の動きをリアルタイムに表現できれば良いです。
ゲームの流体計算の方法については色々文献があるみたいですので、こちらに貼っておきます。
格子ボルツマン法を選んだ理由
話がだいぶそれましたが、格子ボルツマン法を選んだ理由ですが答えは単純です。
コードが簡単だからです。コードが簡単かつ相応の精度で流体計算が行えるためです。
コードが簡単ということは記述が楽というだけでなく、計算にかかる時間もそれほど多くないということです。要は楽したかったということです😃😃
2次元だと以下の方程式を解けば良いので非常に楽でした。こういうやつです。
・・・・実際にはこちらのサイトにあるものを参考にしたり、本を読んだりして結構勉強しました😫😫(疲れた・・・)
気づいた方がいるかもしれませんが、上記でナビエ・ストークスを説明しておきながら格子ボルツマン法はナビエ・ストークスを直接計算しているわけではないです。
もとは気体の分子運動論からきていて、粒子の移動と衝突から流体の流れを記述するものです。
詳しくは理解できていませんが、空気を構成する気体の粒子が移動して衝突している現象を格子上で計算して流体の挙動を模擬しているような感じです。気体も流体ですよね。
なので気体分子の速度が従う分布関数を記述するマクスウェル分布と呼ばれるものが方程式のもとになっています。
先程ナビエ・ストークス方程式を直接解いていないと述べましたが、結局この格子ボルツマン法のもとの方程式をしこしこ解いていくと近似されたナビエ・ストークス方程式が現れるみたいです。
結局流体の挙動を解いているということですね!すごく面白いです。長々説明してきましたがこれが言いたかっただけです😅😅。
勉強は苦手です
Unityで流体計算+描画
さて、理論の部分はこれくらいにしてこれをUnityに実装したいと思います(これもめちゃくちゃ大変だった;;)。
今回は最終的にうまくいった方法と最後にそれまでの試行錯誤の記録を残したいと思います。
テクスチャーを作成して表示する
参考にしたサイトはこちらです。
今回の考えている流体計算の描画の流れはこんな感じです。
流体計算の結果を使ってテクスチャーを作成して、それを貼付けるといった流れです。
テクスチャーとは?
- オブジェクトの質感や見た目を変えるものです。テクスチャーを作ればオブジェクトにドラッグ&ドロップで貼り付ければOK!
僕がいっぱいいるんだよ~
まずHierarchyのUIのCanvasを作成します。
その後作成したCanvasを右クリックしUIからRaw imageを選択します。
するとCanvasの中にRaw imageができます。
このCanvasに流体計算の結果を描画します。Raw imageは流体計算の結果を貼り付けるためのコンポーネントです。
続いて今回のキモである流体計算とテクスチャの作成と貼付けのスクリプトを作成します。作成したスクリプトのコードを以下に示します。流体計算のところは結局長いのでそれ以外の部分を載せます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#Unity C# using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Water : MonoBehaviour { float width = 0f; float height = 0f; private Texture2D m_texture; private Color col; public RawImage raw; // Start is called before the first frame update void Start() { m_texture = new Texture2D(num_x, num_y, TextureFormat.ARGB32, false);// //Texture2Dコンストラクタ(第一引数はテクスチャの幅、第二引数はテクスチャの高さ、第三引数はテクスチャフォーマット、第四引数はミップマップ) width = raw.GetComponent<RectTransform>().sizeDelta.x;//RawImageの幅 height = raw.GetComponent<RectTransform>().sizeDelta.y;//RawImageの高さ raw.color = Color.white;//RawImageの色 raw.texture = m_texture;//テクスチャを割り当てる } void FixedUpdate()//流体計算とテクスチャの作成と描画 { //流体計算// //テクスチャの更新// for (int y = 0; y < 75; y++) { for (int x = 0; x < 100; x++) { u2 = Mathf.Clamp01(((float)System.Math.Pow(us[x, y], 2)) + ((float)System.Math.Pow(vs[x, y], 2))); //速度の絶対値 col = new Color(1f - u2, 1f - u2, 1f - u2, 1.0f); // Red/Green/Blue/Alphaの順に色指定. m_texture.SetPixel(x, y, col); // ピクセルごとに色を割り当てる } } m_texture.Apply();//色の変更を適用する }//Fixed }//class |
スクリプトを作成したら、Hierarchyの2D ObjectからSpriteを選択してそこに作成したスクリプト(今回は「Water」という名前)をドラッグ&ドロップで関連付けます。
また、SpriteのWaterのスクリプトのWaterに(ややこしい)Raw imageを関連付けます。
これで操作は完了です!後は適当に流体計算のパラメータをいじって再生します。
以下の上の動画は初期条件として左側の境界に速度を与えたものと、下の動画は初期条件として領域の中心付近に圧力を与えたものになります。
おおおおおおおおおーーー!
なんとなくそれっぽいのができました。しかもリアルタイムで描画されています。
計算格子としては幅100×高さ75になります。下の方の動画だと少し描画の粗さが目立つような気がします。
そこで格子数を倍の幅200×高さ150にして計算してみました。すると、
粗さはなくなっていると思います。
しかし、動画にして等速にするとスムーズに見えますがこれ実際にはカクカクです。とてもじゃないですが、リアルタイムでは難しそうです。ん~ここは改善の余地がありそうです。
番外編:苦労した点
今回の作業で苦労した点としてはテクスチャを貼り付ける作業です。
はじめはテクスチャの色を直接変えようとしていたのですが、どうやら2Dだとうまく行かないみたいです。
こちらのサイトのように2Dオブジェクトにテクスチャを貼り付けてやろうと試みたのですが、マテリアル自体の色は変えられるのですが、テクスチャの色が変わってくれませんでした。
原因はよくわかりません😫。当然3Dだとうまくできました。
また、シェーダーと呼ばれる描画方法を記述したプログラムを別に作る方法も考えました。
流体計算からシェーダーに値を送ってシェーダーで描画の表現を変えるという方法です。こちらなどを参考にしました。
これはまずシェーダーを理解するのが難しいというのと、一定の色に変化させるのはできたのですが、ピクセルごとに変化させるというのはわからず断念しました。
本屋にも何件か行ったんですが、今回のような作業はニッチなのかなかなか打開策が見つかられなかったです。
そしてもう諦めようとしていたときに今回の方法を見つけました。本当に良かったです。
まとめ
今回はUnity2Dで流体計算を自分で行ってそれを描画するということをやってみました。まとめを以下に示します。
- 流体挙動はナビエ・ストークス方程式を計算することで再現可能
- 格子ボルツマン法は比較的簡単に記述可能
- 流体計算結果を使ってテクスチャを作成しRaw imageに貼り付けることで結果を描画可能
流体計算とその結果の描画はできたもののまだまだ改善の余地はありそうですので、今後もっと勉強して実際にゲームに使えるように改善していきたいと思います。
参考サイト,文献
- 蔦原道久:格子気体法・格子ボルツマン法-新しい数値流体力学の手法-, コロナ社
- 荒木健, 越村俊一 :格子ボルツマン法による自由表面流れの解析, 土木学会論文集B2, Vol. B2-65,No.1,2009,056-060.
- uGUI/Texture2Dで描画するリアルタイム流体計算
- Unity でShaderの勉強 その1
- unity_script_create_texture2d
- Textureをスクリプトから編集してみる
コメント