VR 研究記

3D技術の経験を活かして、VRに挑戦するおっさんの奮闘記

【Shaderプログラミング】雪を降らせる手法について Final - The End -

いよいよ最後になります。
今回は、ソースコードを公開して、各レイヤー別に説明を入れていきます。

シェーダープログラミングの書き方やどこの部分が何を表現しているかを確認出来れば応用が利くのではないかと思います。
なので、それぞれに公式の説明ページのリンクを張りながら書いています。

ぜひ、創作をしてみて下さい。

Let's try it.

雪のシェーダープログラミングのソース

ソースは、下記の通りになります。
※実現するために必要な最小限のコードとして公開しています。
※コピペ問題を無くす為に、あえて画像にしています。
f:id:subrutm:20170511144414p:plain

上から順に説明を入れていきます。
ここからは、下記の記事にて構文の前提知識を入れた上で読んでください。
subru-vr-research.hateblo.jp

SubShader内のTags

SubShader内のTagsは、『いつどのようにしてレンダリングエンジンでレンダリングするか』を定義するポイントです。
設定可能なタグについては、下記のリンクにあるので、ご自分で確認してください。

docs.unity3d.com


今回は、Replacement Shadersを利用してみます。
基本的なシェーダーなら、【Opaque】を指定すれば大体は賄えます。
詳細な仕様は、本家で確認してください。

docs.unity3d.com

描画モードの設定

[#pragma]を使って、描画モードを設定します。

今回は、頂点シェーダーを設定するので、下記のように書きます。
#pragma surface surf Standard fullforwardshadows

初期値と変化はありませんが、詳細な説明は公式の下記を参照ください。
docs.unity3d.com


余談ですが、頂点シェーダーを使って『FinalFantasy 15』のようにクルマを凹ましたりするなら、Vertex shaderを利用すると良いでしょう。
その場合、vertex:(vertex function name)という形になります。
(vertex function name): 任意の名前


関数を下のほうに用意して、関数ポインタをリンクする。
書き方は、下記の通りになります。
#pragama vertex:vert

// 変数定義など入りますが、省略しています。

void vert(inout appdata_full v) {
// 頂点処理
}

変数定義

次にGUIからのパラメータを書く領域になります。

今回は、下記のパラメータと構造体を設定しています。
sampler2D _MainTex; // テクスチャーデータ
float _Snow; // 内積閾値 (Part.5での『条件』にあたる)
float4 _Color; // オブジェクトの色
float4 _SnowColor; // 雪の色
float3 _WindDirection; // 風のベクトル

※ここの変数名が、GUIの定義(Propertyレイヤー)と連動しているので注意が必要です。

構造体定義

次に構造体の定義を記述しています。
主にテクスチャ座標を設定します。

詳細な仕様は、
docs.unity3d.com
サーフェスシェーダーの input 構造体』

今回は、以下のように記述しています。
これらの情報は、メッシュデータに含まれてまして、3Dレンダラーに追加するものです。
struct Input {
float2 uv_MainTex; // テクスチャのマッピング座標
float3 worldNormal; // ワールド座標系の法線ベクトル
};

サーフェイスの描画制御

いよいよ、前回の記事で考察したロジックを組んでいきます。

法線(Normal)ベクトル N(n_1, n_2), 重力(Gravity)ベクトル G(g_1, g_2), 風力(Wind)ベクトルを W(w_1, w_2)とするとき、
条件を  \cos \theta = {\frac{n_1w_1 + n_2w_2}{\sqrt{n_1^2 + n_2^2}\sqrt{w_1^2 + w_2^2}}} ≒ 1.0とするでしたよね。

サーフェイスシェーダーを書いていきます。
※ここは、肝になるポイントなので詳細に抑えていこうかと思います。

                                                                                                                                                                                      • -

関数定義は、下記の通りです。
void surf (Input IN, inout SurfaceOutputStandard o)

Inputは、先ほどの【構造体定義】の章で記載しました。
SurfaceOutputStandardは、物理ベースのレンダリングエンジンに利用する構造体です。

以下のページの【どのように機能するか】の欄に構造体の内容が書かれています。
docs.unity3d.com

                                                                                                                                                                                      • -

条件になる記述は、以下の部分です。
dot(WorldNormalVector(IN, o.Normal), _WindDirection.xyz) >= _Snow

WorldNormalVector関数は、ワールド座標系に合わせた面の法線ベクトルを算出してます。
dot関数は、内積をしてまして、面の法線ベクトルと風ベクトルの内積を計算してます。
_Snowが、閾値になります。

                                                                                                                                                                                      • -

最後に、条件が正のとき、雪を描画する処理を追記します。
出力側の色を変えるので、SurfaceOutputStandardの色を変更しましょう。

色を変更するには、SurfaceOutputStandardの仕様を確認すると分かります。
fixed3 Albedo; // base (diffuse or specular) color (基本色という意味)


なので、o.Albedoで色を変更できます。
この変数に、雪の色としてインプットされたデータを代入することで、指定の面の色が変わります。
結果、条件に合った色が面に載るため、雪がのっているように見えるというカラクリでした。

まとめ

シェーダープログラミングの各レイヤーと書き方が理解できれば十分だと思います。
UnityでのGUI部の作り方からレンダリングパイプラインでメッシュデータを弄る方法などを公開しました。

これで、雪を降らせるプログラミングの説明は終わりです。

シリーズ一覧:
【Shaderプログラミング】雪を降らせる手法について Part.1 - VR 研究記
【Shaderプログラミング】雪を降らせる手法について Part.2 - VR 研究記
【Shaderプログラミング】雪を降らせる手法について Part.3 - VR 研究記
【Shaderプログラミング】雪を降らせる手法について Part.4 - VR 研究記
【Shaderプログラミング】雪を降らせる手法について Part.5 - VR 研究記