UnityのScriptable Render Pipeline(SRP)で最初の導入をやってみる

# UnityのSRPで最初の導入をやってみる
SPRの概念などに関しては、多くの方々が素晴らしい記事や講演をされているので、そちらをご覧ください。

LWRPがUnity2019.3でUniversalRPと名前を変えたようです。
ここでは、最初の最初、新しいSRPのセットアップを行い、簡単な画面が出るところまでやってみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12340/0609b8c5-7218-b46a-519f-ab363d082ee1.png)

---
Unityバージョンは2019.3.0b6
com.unity.render-pipelines.core 7.1.1
com.unity.render-pipelines.lightweight 7.1.1
com.unity.render-pipelines.universal 7.1.1

を利用しています

## 手順まとめ
[コードを読んでみる](#コードを読んでみる)
[RenderPipelineクラスを継承してクラスを作る](#renderpipelineクラスを継承してクラスを作る)
[RenderPipelineAssetクラスを継承してクラスを作る](#renderpipelineassetクラスを継承してクラスを作る)
[RenderPipelineAssetの作成](#renderpipelineassetの作成)
[RenderPipelineのRender関数内部の記載](#renderpipelineのrender関数内部の記載)
[RenderPipelineに即したシェーダとマテリアルの作成](#renderpipelineに即したシェーダとマテリアルの作成)

上記手順にそってやってみます。

#### コードを読んでみる
PackageのコードはLibrary\PackageCacheフォルダの中に含まれています。

まずUniversalRenderPipeline.csを見てみます。このクラスは<b>RenderPipeline</b>を継承しています。
クラスの中にRender関数があります。

```
protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
{
    BeginFrameRendering(renderContext, cameras);

    GraphicsSettings.lightsUseLinearIntensity = (QualitySettings.activeColorSpace == ColorSpace.Linear);
    GraphicsSettings.useScriptableRenderPipelineBatching = asset.useSRPBatcher;
    SetupPerFrameShaderConstants();

    SortCameras(cameras);
    foreach (Camera camera in cameras)
    {
        BeginCameraRendering(renderContext, camera);
#if VISUAL_EFFECT_GRAPH_0_0_1_OR_NEWER
        //It should be called before culling to prepare material. When there isn't any VisualEffect component, this method has no effect.
        VFX.VFXManager.PrepareCamera(camera);
#endif
        RenderSingleCamera(renderContext, camera);

        EndCameraRendering(renderContext, camera);
    }

    EndFrameRendering(renderContext, cameras);
}
```

いいですね、いかにも僕レンダリングしてます！感があります。
レンダリングを行うためにはこの関数とクラスが必要そうです。

次に、このRenderPipelineを作成している箇所を探してみます。

```
protected override RenderPipeline CreatePipeline()
{
    // 色々処理していますが、省略。
    return new UniversalRenderPipeline(this);
}
```

ありました。UniversalRenderPipelineAsset.csです。
このクラスは<b>RenderPipelineAsset</b>を継承しています。

RenderPipelineAssetのCreatePipelineから、RenderPipelineを作成する流れのようですね。

----
RenderPipeline
RenderPipelineAsset

上記2つのクラスは、UnityEngine.Renderingの名前空間にいて、UniversalRPの中に含まれるコードではありません。
UniversalRPを使わないパイプラインの、最初のセットアップとしては、上記2つのクラスを利用する必要がありそうです。

#### RenderPipelineクラスを継承してクラスを作る
まずRenderPipelineクラスを作成します。
RenderPipelineを継承してクラスを作成します。

```
public class CustomRenderPipeline : RenderPipeline
{
    protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        //ここにレンダリング処理が入る
    }
}
```


#### RenderPipelineAssetクラスを継承してクラスを作る
次に、PipelineAssetクラスを作成します。
RenderPipelineAssetを継承してクラスを作成します。

```
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        //パイプラインの作成
        return new CustomRenderPipeline();
    }
}
```


#### RenderPipelineAssetの作成
RenderPipelineAssetは、ScriptableObjectを継承しているクラスとなっていて、実際に利用するためには、Assetを作成する必要がありそうです。

```
public class CustomRenderCreate
{
    [MenuItem("Assets/Create/MySRP")]
    public static void CreateSRP()
    {
        var instance = ScriptableObject.CreateInstance<CustomRenderPipelineAsset>();
        AssetDatabase.CreateAsset(instance, "Assets/MyScriptableRenderPipeline.asset");
        GraphicsSettings.renderPipelineAsset = instance;    //勝手に設定してくれている
    }
}
```
上記コードは
https://connect.unity.com/p/scriptablerenderpipelinewozi-zuo-shitemita
のコードを参考にさせて頂きました。

ここまでやると、ゲーム画面はこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12340/2e13acd6-74b2-39c0-507b-f2edabff7845.png)

とりあえず画面に何も出ないことが確認できました。虚無ですね。ありがとうございます。
（Qiitaさんこんな画像に容量つかってすみません）

#### RenderPipelineのRender関数内部の記載
上記で作成した、CustomRenderPipelineのRender関数には何も記載がないので
当然なにも表示されません。
そこで、最低限のレンダリングを行うコードを書いてみます

あまり長くないので全文載せます

```
public class CustomRenderPipeline : RenderPipeline
{
    void SortCameras(Camera[] cameras)
    {
        Array.Sort(cameras, (lhs, rhs) => (int)(lhs.depth - rhs.depth));
    }
    
    /// <summary>
    /// レンダリング実装箇所
    /// </summary>
    /// <param name="context"></param>
    /// <param name="cameras"></param>
    protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        BeginFrameRendering(context, cameras);

        GraphicsSettings.lightsUseLinearIntensity = (QualitySettings.activeColorSpace == ColorSpace.Linear);
        
        SortCameras(cameras);
        
        // カメラごとに処理
        foreach (var camera in cameras)
        {
            BeginCameraRendering(context, camera);
            
            // カメラプロパティ設定
            context.SetupCameraProperties(camera);
            
            //カリングの実行
            CullingResults cullResults = new CullingResults();
            ScriptableCullingParameters cullingParameters;
            if(!camera.TryGetCullingParameters(false,out cullingParameters)) continue;
            cullResults = context.Cull(ref cullingParameters);

            //フィルタリング、ソート
            SortingSettings sortingSettings = new SortingSettings(camera) {criteria = SortingCriteria.CommonOpaque};
            var settings = new DrawingSettings(new ShaderTagId("lbDeferred"), sortingSettings);
            var filterSettings = new FilteringSettings(new RenderQueueRange(
                0,
                (int) RenderQueue.GeometryLast), 
                camera.cullingMask);
            
            //描画処理
            context.DrawRenderers(cullResults, ref settings, ref filterSettings);
            
            EndCameraRendering(context, camera);
        }
        
        //サブミット（終了処理）
        context.Submit();
        EndFrameRendering(context, cameras);
    }
}
```

実際のレンダリングのコードは、以下講演から引用させて頂きました。
https://www.youtube.com/watch?v=nLjT1bwz2P4

上記処理について細かく説明している＆今年行われた、実践的で分かりやすい講演なので、ぜひご覧になってみてください。
#### RenderPipelineに即したシェーダとマテリアルの作成
```
Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags { "LightMode" = "lbDeferred" }
            
            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
        }
    }
}
```
上記シェーダを利用したマテリアルを作成して、適当なテクスチャを貼り付けると、下記のような画面が出ます。
だんだん雑になってきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12340/0609b8c5-7218-b46a-519f-ab363d082ee1.png)

以上！

##### 余談
Packageのコードを読もうとすると、VisualStudioだとPackageの参照が正常に読み込まれず
Preference→ExternalToolsのGenerate all .csprojをONにする必要がありました。

ただ、VisualStudioだとPackage内部のコードにはブレークポイントが貼れませんでした。。。
(Riderを使ってみると、ブレークポイントが貼れました)
ここらへんお詳しい方いたら、ご教授いただけると助かります (*_ _) 

参考リンク
https://connect.unity.com/p/scriptablerenderpipelinewozi-zuo-shitemita (少し古め)
https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/ (おすすめ。英語ですが、SRP関連の情報が多い)
https://www.youtube.com/watch?v=nLjT1bwz2P4 (おすすめ)