Shaderお絵かきを初めて行うためのアプローチ

この記事は、UnityAdventCalender2019その2の19日目の記事です。



まだ記述途中です。22日中には記述終える予定です。


# 対象読者
- シェーダーお絵かきをしたい人
- シェーダーお絵かきしたいが、アプローチをとれば良いかわからない。
- どこから初めて良いかわからない人
- 板ポリに対して、色んな絵を記述できる（かも？）
- シェーダーで何か作りたいけどどういう風のつくり方をしていいかわからない


# この記事で得られる知見
 - シェーダーお絵かきのアプローチがわかるかも？

# 前提知識
- テクスチャのUV座標とかがわかる


# 今回しないこと
- がっつりなシェーダー内容（絵作り的な意味合いのもの）

# 今回の環境　

Unity2019.3.0f
ShaderGraph 7.1.6
LightWeightRP 7.1.6


本当はUnity2018の環境で行う予定でしたが以下の理由により、ShaderGraphが開くことができない状態の模様なので、Unity2019に移行しました。

### Unity2019で行った事情
UnityのVer2018.4.9fにてShaderGraphの4.0以上を使った場合は、そもそもShaderGraphを使用できずにそもそも立ち上げることなく以下のエラーを吐き出す場合があります。
上記のUnityVerと4.0もしくは4.10を行なった際には、少なくとも、私の環境では発生しました。

同じくUnityForm上で、以下のやりとりが行われているので、起きる場合は往々にありそうです。
https://forum.unity.com/threads/shader-graph-crashes-when-opening-graph.661312/

解答としては、Unityの2019のverにあげて、そのタイミングのLightWeightRenderPipline(UniversalRenderPipeLine)にて行うことができたので、そちらに移行

追記：なお、Unity2018.4.7fとShaderGraph 4.10の組み合わせだといけるっぽいので、組み合わせ次第だといける場合はある模様です。


# 目次
- ShaderGraphの使うまでの前準備
- アプローチの仕方の基本
- シンプルな表現
- 例題に対しての分解のアプローチ


#ShaderGraphの使うまでの前準備

メニューバーにある　Window/PackageManagerでPackageManagerを開きます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/294f2f37-d294-bca1-fab5-d4138f775582.png)

ShaderGraph　と　UniversalRPをinstallします。


ShaderGraphを使用する上でPipeLineAssetを設定する必要があるので
Project画面の上で、右クリック/Create/Rendering/UniversalRenderPipeLine/PipeLineAssetを選び作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/9afa0511-ab8a-3213-322b-119d51046a41.png)

作成すると以下のファイルが作成されます。
UniversalRenderPipelineAsset.asset
UniversalRenderPipelineAsset_Renderer.asset

この2つのうちのUniversalRenderPipelineAsset.assetを
メニューバーのEdit/ProjectSettingで開かれる以下の画面からGraphicsという項目を選択し、赤枠部分に、ドラッグ&ドロップを行います

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/4ad47522-aa97-8800-5f96-cda359bed6de.png)

次にProject画面部分で、右クリック　Create/Shader/UnlitGraphで.ShaderGraphファイルをダブルクリック
これで準備は完了

# アプローチの仕方の基本
Shaderお絵かきを行うに当たって、「要素の分解」というのが重要です。
例えば、以下のような場合（縦に縞模様が動きながら、点滅するようなシェーダー）のものだと

今回作るものはこちら
![ShaderGraph.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/94dee7c6-6d5c-a2a6-d67d-69cce3bfa6e8.gif)


縞模様が動きながら点滅を行えるようになるものです。

要素としては
- 点滅
- 移動する縞模様
  - 縦に一定間隔で表示（縞模様）
  - 移動する
-αを適用する

といった感じの要素に分解されます。
これらの要素の組み合わせを行う事で、作りたいものに対して、どのようなアプローチをすれば良いかがわかります。

それでは、それぞれの最小の部分では、どういった要素によって組み合わせられているでしょうか？
## 点滅
ノードとしては以下の組み合わせになります。
Time + Sin (+ Absolute)

Sinノードは、与えられた値を、Sinの式に対して当てはめます。
0−2の間に対して、−1〜1の値が出力として返却されるので、以下のようなものが出力として存在します。


上記の二つのノードを組み合わせることで
Timeの値をSinに与えて、Sinで出力したものを色として扱って出力を行う事で、点滅ができます。

何故点滅が行われるかというと
Sinノードから出力される値は、あくまでも−1　〜　1の値が出力されます。
これを色情報（X,Y,Z,W(アルファ))に対して、Sinノードからの出力を設定しているためおきます。
プログラム的には、

float　c　＝ sin(Time.deltaTime);
Vector4(c,c,c,c);

みたいなイメージでしょうか？

ただし、あくまでも白くひかるタイミングというのは、0を超えたタイミングとなるため
Sinから出力される値が、負の値の場合は黒くなっているため、比較的に長く暗くなっているように見えてしまいます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/46cd561b-2780-a1f4-1134-61e0a20891ff.png)


そこで、Absoluteノードが役に立ちます。

Absoluteは入力された値の絶対値を出力するためのものです。
絶対値というのは、大雑把にいうと、マイナスでも　マイナスの値をとった数値のみを取り出すというものです。
厳密には、少し違うので、きになる場合は、調べてください。

このAbsoluteを使用する事で、先ほど、の負の値の部分が、正の値になるので、
以下のような感じになります。

TODO:変化したSinの値を変化させる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/f7bf8b5a-14d2-0eb3-c377-ff69b162a066.png)


## 縞模様
縞模様を作る時には、どういうアプローチがあるでしょうか？

縞模様を作成する場合は入力に工夫が必要です。
理由としては、単純に、最終的な出力に大きく関わるもののためです。
この縞模様は、最終的に出力を行なった際に、この模様をそのものを使用するためです。
「その場所の際に、この色を塗る」という状態にしなければいけません。
その場所の際にというものは、情報のあり方は色々とあります。
・光が当たらない状態の場所
・UV座標のこの位置
・（Local World）Positionのこの位置

などの、その場所を特定する何かで、色を塗らなければいけません

場所とかを表す際に、今回はUV座標を使います。
UV座標は、基本的には0－1の値をXYの部分に入っているので、これらの範囲を使用します。
ただ、これをそのまま使うとあくまでも、0-1なので、暗い部分から徐々に白くなるような以下のような感じになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/88e7c8b6-7b42-a7f1-b1df-133f0e3d6e0b.png)

これをこの1枚の中で何回もでてくると、いい感じに縞模様になりそうです。
イメージ的には以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/8b6ec609-322f-a2a3-246f-3ba79007ed47.png)


これを組むためのシェーダーグラフは以下です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/c009d49e-c351-f740-7aaa-e408786cbe4a.png)

受け取ったUV座標はあくまでも0-1しかないので、これをmultiplyで5倍する事で、0－5の値に変更してます。
0-5をmoduleノードで指定した値で割った余りが出力されます。
今回は1で除算する事で、0－5の値は、0－1のみで出力されます。

例えば、UV座標の0.1のタイミングと、0.5のタイミングの場合
0.1－＞0.5－＞0.5
0.5－＞2.5－＞0.5
といった感じになります。
これによって縞模様が完成しました。


## 移動する
今回でいうならば、入力自体の値を変化するという必要があります。
つまるところ「変化し続ける何か」が必要になります。
今回は、Time部分を使用することにします。

これを Moduloに与える前に足しこむことで、
以下のような図になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101366/5f474fb5-bd6d-8777-e596-9b9a35dfe282.png)

これによって、縞模様が出力される場所がずれ込むことになるので、それによって、縞模様が移動しているように見えることになります。
原理的には先ほどの縞模様で、のmodule部分に入る前にTimeの値が足されています。
このことによって、0－5　＋　Time部分の値になりそれを1で割る事になります。

## αを適用する

αの適用自体は、最終的な出力部分になるので、UnlitMasterノードのAlpha部分につなぐ事で、行う事が出来ます。