Unityを使ってShaderの勉強を始めよう

# はじめに
[前回](https://qiita.com/memedoya/items/c27a2ace319fbdcf475e)はShaderについて概念的な話をしました。
今回は実際にUnityでShderを書きながら学んでいけたらと思います。
ただし、Shaderの勉強が目的なのでUnityの詳しいところはあまり説明せずに進めます。

# 何故Unityなのか
UnityならすぐにShaderが書けます。
逆にこういったゲームエンジンを使わない場合はDirectXやOpenGLといったグラフィックスAPIを使って1から書いていくことになるのですが、初期の勉強に使うにはハードルが高く、始めたばかりの人ならば息切れしかねません。
もちろん理解を深めるためにはグラフィックスAPIのことも知った方がいいので、UnityでShaderに慣れてきたらそっちも勉強しましょう。

# Unityのインストール
詳しい手順は省略します。
バージョンも適当でいいですが、LTS版がおすすめです。
Unityは毎年メジャーアップデートを繰り返していますが、LTSはその各バージョンでの安定版といったところです。

# プロジェクトの作成
テンプレートの選択があります。
Built-in(3Dとだけ書いてあるやつ)でもいいんですが、ShaderGraphが使いたいならURPかHDRPを選びましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/9b66ee76-2593-fa57-7369-7853378af1c7.jpeg" width=80%>
この辺りは説明すると長いので用語についてだけ
|用語|正式名称|説明|
|:-:|:-:|:-:|
|Built-in|Built-in Render Pipeline|従来のRender Pipelineで最近新しくSRPというものが出てきている|
|SRP|Scriptable Render Pipeline|Built-inに替わるカスタマイズに対応したRender Pipeline
|URP|Universal Render Pipeline|Unityが提供している構築済みSRP(スマホ向け)|
|HDRP|High Definition Render Pipeline|Unityが提供している構築済みSRP(PCコンシューマ向け)|

# Shaderの作成
かなり端折りましたが、ここまで来れましたか？
次はShaderを作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/e0dc8678-7207-d15a-1868-034176ce1106.jpeg" width=80%>

画像の赤枠内で右クリックをするとメニューが出るので、
Create > Shader > Unlit Shader と選択してShaderを作成してください。
![スクリーンショット 2022-06-22 201748.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/6bc83f65-f237-33aa-6293-cb92a1dc8c76.jpeg)
NewUnlitShader.shaderというファイルが作成されます。
ではテキストエディタで開いてみましょう。

```NewUnlitShader.shader
Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}
```

これが僕らの勉強の場となるShaderファイルです。
これはUnity独自のShaderLabという構文でCGPROGRAM ~ ENDCGで括られた区間がShaderコードになります。
Cg言語なのでCGPROGRAMというわけですね。

ShaderLabは単にShaderコードだけでなく、プロパティの定義、パスの定義、ブレンドや深度テストの設定など出来ることが多いです。
この辺もOpenGLやDirectXで同じことをやろうとすると面倒なので、最初の勉強としてはやはりShaderLabがいいですね。

# 動作確認環境
早速Shaderコードを書きたいところですが、その前に動作確認環境を作りましょう。
まずはNewUnlitShader.shaderを適用したマテリアルを作成します。
先ほどの赤枠内のShaderファイルを右クリックしてメニューを出し、
Create > Material と選択してマテリアルを作成します。
うまくいけば選択したShaderが適用されたUnlit_NewUnlitShader.matが出来上がります。
![スクリーンショット 2022-06-22 205600.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/4d9ed431-7706-90a2-2cb3-66597329df17.jpeg)
次にUnlit_NewUnlitShader.matを適用したメッシュを作成します。
Unityは球や箱など、簡単なプリミティブであればすぐ作れます。
HierarchyというWindow内で右クリックをしてメニューを出し
3D Object > Sphere でSphereを作ります。
![スクリーンショット 2022-06-22 205917.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/5d76aa59-c672-8f29-c881-384897c0a1f2.jpeg)
できましたね？
この球にはデフォルトのマテリアルが適用されているのでUnlit_NewUnlitShader.matで上書きしましょう。
Sphereを選択してInspectorというWindowを見てください。
![スクリーンショット 2022-06-22 210314.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/1998976b-67cb-e660-70d0-ebeb6d52dac1.jpeg)
Materialsが閉じてて見えないので、クリックして開いてください。
するとLitというデフォルトのマテリアルが適用されているのが見えます。
これをProjectというWindowからUnlit_NewUnlitShader.matをLitのところにドラッグします。
![スクリーンショット 2022-06-22 210547.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/576da005-699e-8134-a027-da43e9cd9a96.jpeg)
こうなれば適用完了です。
では球がどうなっているか見てみましょう。
![スクリーンショット 2022-06-22 210826.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/4930b271-2a8b-86fe-f614-66f98813a43a.jpeg)
はい、Global Volumeの表示が邪魔なのでHierarchyから削除しましょう。
![スクリーンショット 2022-06-22 211003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/4e381e8f-f2d8-44ba-8f1a-7b4d798c2b0a.jpeg)
右クリックメニュー > Delete でもいいですし、Deleteボタンか何かでも消せると思います。
Global Volumeはいわゆるポストエフェクトというもので、グラフィックス的にはすごく重要なのですが今は邪魔です。
![スクリーンショット 2022-06-22 211252.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/0fb4d11b-586a-2280-ad66-f84fa2b506cb.jpeg)
これでやっとNewUnlitShader.shaderを適用したメッシュが表示できました。
デフォルトの白テクスチャを貼り付けているだけの状態なので真っ白な球体になりました。

# Shader何か書いてみる
最後にNewUnlitShader.shaderのFragmentShaderを弄ってみて球体がどうなるか見てみましょう。
これがFragment Shaderです。
```
fixed4 frag (v2f i) : SV_Target
{
    // sample the texture
    fixed4 col = tex2D(_MainTex, i.uv);
    // apply fog
    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}
```
こいつ自体はfragという名前の関数にすぎないのですが、上の方の#pragmaという文でこのfragがFragmentShaderであると設定しています。
```
#pragma fragment frag
```
では関数の中身を消して緑色を返すコードを書いてみましょう。
```
fixed4 frag(v2f i) : SV_Target
{
    return fixed4(0.0, 1.0, 0.0, 1.0);
}
```
fixed4がいきなり分かりにくいですが、float型よりもサイズ(精度)の低い4要素Vector型といったところです。
1要素目から順に赤、緑、青、アルファといういわゆるRGBAで解釈され、画面のピクセルの色となって出てきます。
![スクリーンショット 2022-06-22 213208.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/70fd10cb-7d18-9c88-7cc3-b5f702075b13.jpeg)
球が緑色になりましたか？
Shaderでは色は全てRGBAで表現します。
例えば、RGBが全部0で黒、全部1で白、RとBが1で紫色という風にあらゆる色が出せるので色々いじってみるといいでしょう。

ちなみにShaderがコンパイルエラーになっていた場合、Unityはピンク色を出してきます。
![スクリーンショット 2022-06-22 213919.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/be603032-bdb1-afa4-a59c-40f26fdda13c.jpeg)
その場合はConsoleWindowなどでエラーが確認できるので修正しましょう。
![スクリーンショット 2022-06-22 214020.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2639462/378b2022-2eae-77db-4948-b38c39f07c83.jpeg)
どうやらfixed4を書きミスっているようですね。

# まとめ
今回はUnityでFragmentShaderをいじってみるところまで書いてみました。
Shaderを書いてすぐに確認までできるので、やはりゲームエンジンは強いですね。
次回もShaderの記事を書こうと思うので、見ていただけたら幸いです。