【ShaderGraph】積雪表現をShaderGraphで作ってみる


本記事は、[サムザップ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/sumzap) の12/22の記事です。
昨日の記事は @Buna_unity_game さんの「[【ScriptableObject】シングルトンを使わずにマルチシーンのイベント管理してみる](https://qiita.com/Buna_unity_game/items/db58a5706b89a68a8976) 」でした。

#目次
[はじめに](#はじめに)
[今回作ったもの](#今回作ったもの)
[関連Tips](#関連tips)
[解説](#解説)
[最後に](#最後に)

# はじめに
去年のアドベントカレンダーで使っていたバージョンから結構アップデートされていて、UniversalRenderPipeline(以下、Universal RP)とShaderGraphが変わっていそうなので、最新のShaderGraphを使用してみようかなと思います。
ちなみに、去年のアドベントカレンダーでの記事はこちら。

| [【ShaderGraph】Quadで擬似的な3D空間を表現したサンプルを紹介](https://qiita.com/kida_hironari/items/7e9255745ff74c1b66f3)<BR>平面の中に擬似的な3D空間をShaderGraph使って実現してみた... | <img width="200" src="https://camo.qiitausercontent.com/a3c63b4c9e7ad2d4ac1d3f489125f6e999cdb025/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3230393838392f33633039613437362d306461632d663664362d336431622d3137396639646461646561382e676966">|
|:--|:-:|

| [【ShaderGraph】トゥーンっぽい炎の演出をShaderGraphで作ってみる](https://qiita.com/kida_hironari/items/7e9255745ff74c1b66f3)<BR>トゥーンっぽい炎の演出をShaderGraph使って表現してみよう... | <img width="100" src="https://camo.qiitausercontent.com/6cbfc79fe7667bbd99219eedf49b77cc1684e4b3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3230393838392f63616335633633352d653864352d356434392d386262612d3738383361646233353065642e676966">|
|:--|:-:|

## 使用環境
今回使用する環境はこんな感じです。

||バージョン|
| :--: | :-- |
|Unity|2021.2.4f1|
|Universal RP|12.1.1|
|ShaderGraph|12.1.1|

前回使用していたバージョンが7.3.1だったので結構変わっただろうな…と思いつつ、やっていきます。
Universal RP,ShaderGraphの設定周りの準備に関する内容は、本記事では省略します。

# 今回作ったもの
今の季節が冬って事もあり、ShaderGraph使って積雪表現を作ってみました。
![スクリーンショット 2021-12-20 22.52.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/78e60815-ff9f-9013-b63b-8a13e2577d9b.png)

# 関連Tips
ノードの説明とかは公式ドキュメント参照って事で、便利だなって思ったTipをメモ的な感じで書いておきます。

## よく使ったショートカット
前回使ってたのにショートカットを忘れたので、作る前にメモ〆

|ショートカットキー|内容|補足|
| :-- | :-- | :-- |
|A|全てのノードが表示されるようにズームイン/ズームアウトする|Preview window、Blackboard、Internal Inspectorに被ってしまうのが使いづらいから、どうにか改善されて欲しいなーと思ったり。|
|F|選択中のノードが全て表示されるようにズームイン/ズームアウトする|Aと同じく、使いづらい部分は多少ある。|
|Spacebar|ノード作成|「Vertex」や「Fragment」などを選択中にSpacebar押下で、中にノード追加出来る。|

※基本的なコピー&ペーストなどは長くなるので省略します。

## Category / Group
グループ化したいノードを選択してGroup Selectionする事で、ノードをグループ化してまとめることが出来ます。
複雑なグラフになるほど、ノード一つ一つのプレビューをオンにしていると全体が見えなくて邪魔になります。
その為、基本的にノードのプレビューは閉じて作業しているので、このGroupを設定しておくと、何をしている処理なのかがひと目で分かります。
ただ、確認のために他グループと混線するノードはプレビューを閉じないようにしておくのが良さそうです。
![スクリーンショット 2021-12-20 23.14.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/32cce257-52ce-339b-f3fc-0f666c63e6d7.png)
Categoryを使用することで、Blackboardにも同じような事が出来ました。
(今回の分け方・使い方で良いのかは分からないですが…)

# 解説
## 全体像
積雪表現に直接関係するところ以外の説明は簡略化します。
![スクリーンショット 2021-12-20 23.16.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/a51de114-9ab8-6060-1303-5bcb3be374ab.png)

## Blackboard
まず、Blackboardから解説していきます。
Category分けをしていてBaseは基本的な構造です。
![スクリーンショット 2021-12-20 22.59.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/6c4092ab-e359-1d64-036d-0935b9a299ea.png)

|パラメーター|説明|
| :-- | :-- |
|SnowDirection|雪がレンダリングされる場所の指定|
|SnowColor|雪の色調整|
|SnowAmount|積雪量の調整|
|SnowBlendDistance|ベーステクスチャと雪レイヤーのブレンド領域制御|
|SnowColorNoiseSize|積雪具合の調整|
|SnowColorNoiseStrength|積雪の厚み調整|
|BuildupNoiseSize|雪の塊具合の調整|

## Normal Texture
積雪表現に関わるノードと混線している訳でもないので、NormalTextureは特筆して説明する要素はなく、ただFragmentのNormalへ値を渡しているだけになります。
![スクリーンショット 2021-12-20 23.02.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/79ccf185-a378-aa42-141e-5ec92fa81bd3.png)

## Dot Product Calculation
指定されたSnowDirectionとメッシュの法線ベクトルとの内積計算をします。
NormalizeでSnowDirectionを単位ベクトルにして内積計算をし、Saturateで0-1間にバインドして出力を飽和させます。
![スクリーンショット 2021-12-20 23.14.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/e596880d-22ab-6c1e-3fec-4adb44d61e1f.png)

## Snow Buildup(Noise Calculation)
雪の塊具合をSampleNoiseを使用して計算し、先ほど計算したDot Product Calculationの結果を乗算することで、積雪表現に必要なMap作成をします。
この時、先ほどと同じようにSaturateで0-1間にバインドして出力を飽和させておきます。
![スクリーンショット 2021-12-20 23.15.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/c6c803fe-33e8-1270-de11-660b88269859.png)

## Snow Color
薄汚れた雪も表現できるようにColor調整出来るものだけでも良かったのですが、現実世界で真っ平に積雪する事は無いわけで…。
Snow ColorでもSampleNoiseを利用して、積雪の厚みを調整出来るようにしてみました。
![スクリーンショット 2021-12-20 23.14.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/6e965d4c-b2a5-a2f8-714a-3bd2bc4d65b2.png)
積雪の厚みが伝わりづらいと思ったので、別画像用意しました。
SnowColorNoiseStrengthが右と左で変わっていて、これで凹凸感も表現出来るようにしています。
![スクリーンショット 2021-12-21 0.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/dcb7c023-d6af-b89d-587e-8a54728b3cd7.png)

## Sample Texture + Base (Albedo) Color
ベーステクスチャと雪レイヤーのブレンドをSnow Buildup(Noise Calculation)の結果の値を利用して線形補間した後、FragmentのBase Colorへ結果を渡しています。
![スクリーンショット 2021-12-20 23.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/b0d18b95-9f9e-e4bf-8885-158aa550fde0.png)

## Smoothness Map
Smoothnessも同様に、Snow Buildup(Noise Calculation)の結果の値を利用して線形補間した後、FragmentのSmoothnessへ結果を渡しています。
![スクリーンショット 2021-12-20 23.15.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/3ae25f3e-f926-67b7-3c9f-59096a375cbe.png)

# 最後に
途中からTiling And OffsetとSampleTexture2Dの組み合わせが複数あったので、サブグラフ化も検討しましたが断念しました。。
![スクリーンショット 2021-12-20 23.45.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/a0044203-53ab-7e65-640b-00d3df853fc3.png)
![スクリーンショット 2021-12-20 23.44.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209889/59e2cca6-bb41-dcaa-e287-52cadef56a30.png)
branchかなんかで、TypeのDefaultとNormalの切り替えを同一のSubGraphで実現できると綺麗なのですが…。
良い方法知っている方いれば、是非教えて欲しいです！！

よく使うノードセット的なものは、SubGraphで使い回しが出来るような形でリソースを溜めておきたいなと思いました。


明日は @chrno001 さんです。