水路などを表現する際、水面下の屈折表現としてGrabPassをよく使用すると思いますが、 MetaQuestなどAndroidプラットフォームの場合はGrabPassが機能しないので、別の方法で表現する必要があります。
今回はAndoroidプラットフォームにて、GrabPassを使わずに水路の水面下の屈折疑似をやってみたいと思います。 積木ではbuiltinのシーン制作として、AmplifyShaderEditorを使用しているのでそれを使用した一例です。
水路っぽい背景と水面を準備
水面のシェーダーとしてはとりあえずTransparentでOpacityとNormalmapを設定し、 NormalMapをスクロールで動かしています。1枚のスクロールだけではただスクロールしているだけに見えるので、 タイリング、スクロールするスピードを変えた2種類のNormalをblendしてスクロールさせています。
現実であれば、屈折の関係で水面下のコンクリートが水面の揺らぎに合わせてゆらゆら動いて見えるもので、これをGrabPassで表現することが多いですが、それが使用できないとなると水面がNormalMapで揺らいでいますが、水面下に見える水路のコンクリートがまったく動いて見えないのでとてもチープな感じになってしまいます。
水面下の揺らぎの疑似表現を加えてブラッシュアップ
水面下の揺らぎは、AmplifiyShaderEditorのLocal Vertex Offsetで代用します。
なので、水面下のコンクリート部分だけモデルを分離して水面下用のモデルとします。
水面下のモデルはLocal Vertex Offsetで頂点を揺らすのである程度メッシュ分割しておきます。
コンクリートのシェーダーがこちら
とてもシンプルにAlbedoとNormalとSmoothnessが入っているのみです。
ここに水面の動きに合わせて頂点が動くようにしていきます。
水面のNromalMapのスクロールで使ってるノードをそのまま持ってきて
Local Vertex Offsetにつなげます。
頂点が動く強さを制御するために
Displace Scaleという名前でFloatを加え、XYZそれぞれ動いてもらいますが
今回に関してはY方向により動いてほしかったので、Yだけ2を乗算して他よりも
大きく動くようにしています。
最後に、頂点カラーのRをマスクとして、頂点カラーがRのところは
全く動かないようにします。
これの意図としては
水面下の頂点すべてが動いてしまうと、水面上のモデルとの境界で隙間が生じてしまうので
水面下モデルのエッジ部分のみ頂点カラーを赤に設定して、それをマスクとしてその部分だけは
頂点が動かないようにしています。
どうでしょうか。
GrabPassを使用していなくとも、水面下モデルの頂点が水面の揺らぎNormalMapと同じノードによって直接上下左右に動いているので水面上から見ると屈折で揺らいでいるように見えます。
これで疑似屈折表現としてはできましたが、水面に関してもう少し手を加えたいと思います。
水面のブラッシュアップ
汚れた水を想定していますが、水ですので水底が浅いほど水はより透明に見え、水底が深いところほど汚れによって不透明に見えるはずです。SSS的な感じ。
水面のLocal Vertex Offsetも使用して水面自体の揺らぎも足してみましょう。
水面のエッジ部分ギリギリまでSmoothnessのスペキュラーがでてると、そのスペキュラーが水面エッジで中途半端に途切れてゲームっぽいポリゴン感がでてしまうので、水面際にはスペキュラーが出ないように。
水面のLocal Vertex Offsetに関しては水面下と同じです。
水面の揺らぎで使用しているNormalMapノードをLocal Vertex Offsetにつないで頂点移動の強度を設定するのみです。
水面のSSS的な表現と水面際までスペキュラーを出さない表現に関しては頂点カラーを利用します。
まず、水面モデルの頂点カラー赤にて、際から中心に向かってグラデーションになるように設定します。
赤ほど透明度が高く、黒くなるにしたがって透明度が下がる(濁ってみえる)ようにするためです。
また、水面エッジのみ頂点カラー緑を設定して、それをマスクとしてその部分だけはスペキュラーが出ないようにします。
際だけ赤と緑の混色となるので、DCCツール上では黄色の頂点カラーとなります。
Amplify Shader Editorに適用します。
頂点カラー赤をマスクとしてOpacity2種をLerp 中心程透明度を下げ、エッジ程透明度を上げます。
頂点カラー緑をマスクとしてSmoothness2種をLerp エッジ部分だけSmooothnessを0にしてスペキュラーが出ないようにします。
どうでしょうか。
Local Vertex Offset によってエッジの揺らぎが表現され、
SSS的な深さによる濁りの違いも表現できました。
今回シンプルな水路をサンプルとして挙げたのでやりやすかったですが、 自然の川底などでも応用できると思いますので、GrabPassを使えなくとも できるだけ水っぽい表現を今後もやっていきたいと思います。