オブジェクト表面を垂れる液体表現 その1
はじめに
Splatoon動画の壁に塗ったインクが垂れるような表現がしたかったので作ってますというお話です。
まだまだ垂れる部分のアルゴリズムを試作中だったりしますが、ある程度形になってきたので現状報告。
液体の法線データも変化していくため、わりと液体が垂れてる感が出てるかと思います。 壁に使っているシェーダーはいつも通りUnityデフォルトのStandard Shaderです。
液体を垂らす方法
前提として、UnityTexturePaintを使っていきます。
いくつか方法があると思いますが、自分が思いついた2つの方法について見ていきます。
液体の垂れを球で近似していく
見えない球(MeshRendererのenabledをfalseにしたもの)を転がして、球が当たった場所にペイントを重ねていくというかなり単純な方法です。
水色の球が液体の垂れる動作を表現していて、球が転がった先が液体の垂れる場所だと仮定して塗っていきます。 この水色の球をレンダリングしないようにするとこんな感じに。
実装自体は既存機能使うだけでノーコーディングなので楽なのですが、処理が重いのと、高さや法線の情報が塗る度に上書きされるだけなので、垂れてる感がイマイチ出てません。
ペイントマップから高さや法線が垂れる部分を計算して書き換える
塗りを行った場所に、インクの高さ情報を持つテクスチャ(ペイントマップ)を作成し、そのデータをもとに画像処理的に「インクの広がり」、「高さ情報の変化」、「法線情報の変化」を計算する方法です。
まず、上記画像のような、塗った場所に白黒でインクの高さ情報を格納するペイントマップを用意します。 このテクスチャは、ハイトマップとして利用することが出来ます。しなくてもいいですが。
ペイントマップに対して垂れる方向をUV座標で与え、高さの情報をもとに画像処理的にインクを広げていきます。
インクを広げるアルゴリズム
冒頭で言及したとおり、垂れる部分のアルゴリズムは試作中です。現在のアルゴリズムは、各ピクセルに対して垂れてくる液体の量を高さから計算して平均を取る単純なものです。 このアルゴリズムで重要になるのは、「質量保存の法則」をある程度守ることで、これをしないとインクの総量が増えたり減ったりするので違和感を覚えることになったりします(言い換えれば、インクの収束条件に配慮する)。 具体的には、処理前と処理後でインクの高さ情報の総量がなるべく変化しないように心掛けます。 例えば、インクの総量が増え続けるアルゴリズムの場合、垂れていくインクが収束せず、ずっと垂れ続けるなんてことになります。
次に、ペイントマップの高さ情報に対して、法線情報を計算し、テクスチャに格納します。
高さの情報から法線を得る方法は以下の記事で。
法線情報を可視化すると、こんな感じになります。
最後に、ペイントマップを参照して、塗った場所(高さが0より上の場所)に色を付けるようにすれば完成です。
オブジェクトの反射と併用するとちょっとおもしろい表現が出来たりします。
さいごに
まだまだ簡単に作っただけで試作段階なので、より液体が垂れてるっぽくするためにどうしたらいいのか考えていこうと思います。