【Unity(Shader)】Shader初心者が送る水面表現①(屈折)

##屈折
今回作るのはタイトル通り水面表現です。
具体的には屈折の表現について頑張ります。


##屈折デモ
![Water.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/1f554e31-b803-b1c5-8d8d-ab016353edf8.gif)

この表現については検索したらほぼ答えのものが出てきたので助かりました。

【参考リンク】：[【Unity】【シェーダ】GrabPassを使って歪みシェーダを作る](http://light11.hatenadiary.com/entry/2018/12/13/221902)

##屈折Shader

```C++:LIGHT11より拝借
Shader "Wave"
{
	Properties
	{
		_DistortionTex("Distortion Texture(RG)", 2D) = "grey" {}
		_DistortionPower("Distortion Power", Range(0, 1)) = 0
	}

		SubShader
		{
			Tags {"Queue" = "Transparent" "RenderType" = "Transparent" }

			Cull Back
			ZWrite On
			ZTest LEqual
			ColorMask RGB

			GrabPass { "_GrabPassTexture" }

			Pass {

				CGPROGRAM
			   #pragma vertex vert
			   #pragma fragment frag

			   #include "UnityCG.cginc"

				struct appdata {
					half4 vertex  : POSITION;
					half4 texcoord  : TEXCOORD0;
				};

				struct v2f {
					half4 vertex  : SV_POSITION;
					half2 uv  : TEXCOORD0;
					half4 grabPos : TEXCOORD1;
				};

				sampler2D _DistortionTex;
				half4 _DistortionTex_ST;
				sampler2D _GrabPassTexture;
				half _DistortionPower;

				v2f vert(appdata v)
				{
					v2f o = (v2f)0;

					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = TRANSFORM_TEX(v.texcoord, _DistortionTex);
					o.grabPos = ComputeGrabScreenPos(o.vertex);

					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
					// w除算
					half2 uv = half2(i.grabPos.x / i.grabPos.w, i.grabPos.y / i.grabPos.w);

					// Distortionの値に応じてサンプリングするUVをずらす
					half2 distortion = tex2D(_DistortionTex, i.uv + _Time.x*0.1f).rg - 0.5;
					distortion *= _DistortionPower;

					uv = uv + distortion;
					return tex2D(_GrabPassTexture, uv);
				}
				ENDCG
			}
		}
}

```

##_Time
時間の経過によってUVをスクロールさせたかったので
_Timeを追記しました。時間が経過すればするほど値が大きくなると理解しました。

_Timeはx,y,z,wを指定して任意の速度に変更可能です。
`(x,y,x,w) = (t/20, t, t*2, t*3)`

ただ、**UVの値って0~1なのに加算し続けてスクロールするのはなぜだろう？**と疑問に思いました。
その疑問に関しては下記リンクの動画を見ればスッキリです。
0~1の範囲を超えても大丈夫ってことですね。

【参考リンク】：[UV座標について](https://qiita.com/Nekomasu/items/d50a4409e48ad77bc7f2#uv%E5%BA%A7%E6%A8%99%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)


##GrabPass 
GrabPassについて、記事にいろんな表現がありましたが、私が一番しっくり来たのは
>このパスが実行される時点のレンダリング結果を取得できる特殊なパス

という説明です。

引用リンク：[【Unity】【シェーダ】GrabPassの説明とシェーダの実装例](http://light11.hatenadiary.com/entry/2018/12/12/000345)

##色を付ける

合ってるかは置いといて色は付きました。

![WaterAddColor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/33841592-086e-8afd-6f2d-298a6bfa914c.gif)

ただ、本来は水に色があるのではなく、
空の色が映りこんでいるはずなのでここらへんは改良の余地有りです。
しかも、背景に乗算して色が乗っているので青くなりませんでした。
どうやったらきれいになるかもう少し調べます。

##色付きの屈折Shader

```C++
Shader "Wave"
{
	Properties
	{
		_DistortionTex("Distortion Texture(RG)", 2D) = "grey" {}
		_DistortionPower("Distortion Power", Range(0, 1)) = 0
		_Color("WaterColor", Color) = (0,0,0,0)
	}

		SubShader
		{
			Tags {"Queue" = "Transparent" "RenderType" = "Transparent" }

			Cull Back
			ZWrite On
			ZTest LEqual
			ColorMask RGB

			GrabPass { "_GrabPassTexture" }

			Pass {

				CGPROGRAM
			   #pragma vertex vert
			   #pragma fragment frag

			   #include "UnityCG.cginc"

				struct appdata {
					half4 vertex  : POSITION;
					half4 texcoord  : TEXCOORD0;
				};

				struct v2f {
					half4 vertex  : SV_POSITION;
					half2 uv  : TEXCOORD0;
					half4 grabPos : TEXCOORD1;
				};

				sampler2D _DistortionTex;
				half4 _DistortionTex_ST;
				sampler2D _GrabPassTexture;
				half _DistortionPower;
				half4 _Color;

				v2f vert(appdata v)
				{
					v2f o = (v2f)0;

					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = TRANSFORM_TEX(v.texcoord, _DistortionTex);
					o.grabPos = ComputeGrabScreenPos(o.vertex);

					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
					// w除算
					half2 uv = half2(i.grabPos.x / i.grabPos.w, i.grabPos.y / i.grabPos.w);

					// Distortionの値に応じてサンプリングするUVをずらす
					half2 distortion = UnpackNormal(tex2D(_DistortionTex, i.uv + _Time.x * 0.1f)).rg;
					distortion *= _DistortionPower;

					uv += distortion;
					return tex2D(_GrabPassTexture, uv)* _Color;
				}
				ENDCG
			}
		}
}
```

##屈折の仕方
Textureに影響して屈折の仕方が変わります。

なぜ変わるのかは完全には理解できませんでした。
下記リンクからノーマルマップの凹凸情報だけ取り出していると勝手に理解しました。


【参考リンク】：[Unity シェーダーチュートリアル屈折表現](http://tsumikiseisaku.com/blog/shader-tutorial-refraction/)


例えば、こういうノーマルマップを用意したとします。
![NRandom200.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/0e81678c-ec9f-a759-c3bd-0932fbdf3f9e.png)

上空から見ると、凹凸情報が反映されているのがよくわかります。

![NWave200.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/ebffc85d-2f65-ccbc-f7ee-ffa2613ff983.gif)


ただ、コメントアウトされている方のコードでもそれっぽくなったのが腑に落ちていません。
凹凸情報の無いただのテクスチャーでも良い感じになっちゃうのは一体なんなんでしょうね。。。

```C++
//half2 distortion = tex2D(_DistortionTex, i.uv + _Time.x*0.1f);
  half2 distortion = UnpackNormal(tex2D(_DistortionTex, i.uv + _Time.x * 0.1f)).rg;
```

---

2019/12/02 追記

本当にほしい値は`UnpackNormal`で変換した`-1~1`だけど、
`0~1`でもたまたまそれっぽく見えてるだけのようです。
(教えてくださった方、スーパー感謝です。)

##ノイズ画像

下記記事で作成した画像を利用して解説します。

[【ImageJ Fiji, Python】砂嵐画像を任意のピクセル数で簡単に生成する方法](https://qiita.com/OKsaiyowa/items/3f963ecf8c9a5b75b0f0)

####20×20
![20.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/ce25956b-b44b-d6bb-5daf-e622e7cb9b6d.png)

![Wave20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/51a22348-d965-a768-f803-b6d0a4823680.gif)

####200×200
![200.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/29ce54b3-bbb1-689c-023b-e6974a463b6f.png)



![Wave200.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/d544f449-de31-9f7e-164c-05f167aa8002.gif)




##おわりに
Shader難しいですね～。早く完全に理解したい。。。

##参考リンク
[Unityシェーダプログラム入門 UnlitShaderの要素を全て解説](https://amagamina.jp/unity-shader/)