【Universal Render Pipeline】ShaderGraphをポストエフェクトとして使ってみる

# はじめに
Universal Render PipelineのForwardRendererDataやScriptableRendererFeatureを利用すると、
レンダリングパイプラインに独自の処理(レンダリングパス)を追加することができます。

今回はレンダリングパイプラインの最後にシェーダーグラフを突っ込み、フォグ風の表現を実現するポストエフェクトを作ってみました。
![before_after_merge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/924b3b86-2d84-150b-7dd4-29b11231af1e.png)


作成したレンダリングパイプラインはCameraコンポーネントのプルダウン上から簡単に切り替えることができてお手軽です。
![change_renderer.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/d62aa36e-a5e9-c370-08b0-17b84c64adc4.gif)



## フォグ風ポストエフェクトの実装について

カメラのデプス値(深度情報)を利用して二つの色をLerpで合成し、それを画面に表示させています。

```glsl:色の計算式
画面に出力する色 = Lerp(カラーA, カラーB, デプス値)
```


## 環境
【Unityバージョン】
Unity2020.1.0a3

【パッケージ】
Universal Render Pipeline - Version 7.1.2

## デプスバッファを使うための設定
パイプラインアセットの **Depth Texture** のチェックを入れ、シェーダーグラフ上でデプスバッファが取れるようにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/ae7625d3-22af-481e-4526-54976b36239f.png)


# STEP1 : マテリアルの準備
## STEP1.1 : シェーダーグラフ作成
Projectビューのメニューから**Create/Shader/Unlit Graph** を選択し、Unlitのシェーダーグラフを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/42367fac-921c-3e0f-3eab-871a09b767c3.png)

今回は**RenderDepthSimple**という名前にしてみました。


## STEP1.2 : シェーダーグラフ編集
シェーダーグラフにTexture2Dプロパティを定義し、名前を **_CameraDepthTexture** にすると、カメラのデプスバッファ(深度情報)が取得できます。
このデプスバッファを利用して、二つの色をLerpノードで合成し、Unlit Masterノードに出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/06afa88c-93d5-b2f7-8ced-e3aef820abc6.png)

_CameraDepthTextureのExposedのチェックはOFFにしておきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/6ba7ddf9-f26a-ae09-69f1-303ffc2c511a.png)

以上でシェーダーグラフは完成です。


## STEP1.3 : マテリアルを作成
シェーダーグラフを右クリックし、マテリアルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/f206ac19-2f75-7c6f-e52e-564b58da30cc.png)

Shader Graphs_RenderDepthSimpleという名前のマテリアルが作成されます。
作成されたマテリアルのシェーダーには先ほどのShaderGraphが設定されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/d9eb4370-7707-3566-050d-2bfc608b4a49.png)


## STEP1.4 : マテリアルをResources以下に配置
**Assets/Resources/Materials** フォルダを作成し、その中にマテリアルを移動させます。
マテリアルの名前は **RenderDepth** にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/a9a78a1a-5616-1411-ed07-927e1ee2bb08.png)

以上でマテリアルの準備は完了です。


# STEP2 : Rendererアセットの作成・カメラへの登録
Rendererアセットを作成し、これをカメラに登録することでレンダリングパイプラインに独自の処理を差し込むことができます。
今回はレンダリングの最後にシェーダーグラフを実行させるようなレンダリングパイプラインを構築します。

## STEP2.1 : Rendererアセットの作成
Projectビューから
**Create/Rendering/Universal Render Pipeline/Forward Renderer** を選択して
ForwardRendererアセットを作成します。
今回は**RenderDepth** と名付けました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/02fddd31-5892-a15a-3c12-540989f19865.png)

### STEP2.2 : Rendererアセットをリストへ登録
自作のRendererアセットはパイプラインアセットのRendererListへ登録すると

CameraコンポーネントのRendererのところから選べるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/efbb5497-1fd2-7498-fc45-f321c99ff52c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/94c88ca8-6bbe-b05c-2462-0cb067f894a2.png)

カメラのRendererを切り替えることで、レンダリングパイプラインを切り替えることができます。

## STEP2.3 : Renderer Featureを作成する
作成したRendererに自作レンダリングパスを追加するためには、**Renderer Feature**を作成する必要があります。

Projectビューのメニューから
**Create/Rendering/Universal Render Pipeline/Renderer Feature** を選択すると
Renderer Featureが作成されます。

今回は**RenderDepthFeature**という名前にしました。(RenderDepthFeature.csが作成されます)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/bebdbc83-b6ba-7096-2016-7a8da94330da.png)

## STEP2.4 Renderer Featureを書き換える
作成したRenderDepthFeature.csを以下のように書き換えます。

STEP1のRenderDepthマテリアルをロードし、カメラの描画をマテリアルで上書きする処理になっています。

```RenderDepthFeature.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class RenderDepthFeature : ScriptableRendererFeature
{
    class CustomRenderPass : ScriptableRenderPass
    {
        public Material material; // レンダリングに使用するマテリアル

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            if (material == null) return;

            var camera = renderingData.cameraData.camera; // 現在レンダリングを行っているカメラ
            var cmd = CommandBufferPool.Get("RenderDepth"); // 適当なコマンドバッファをとってくる
            cmd.Blit(Texture2D.whiteTexture, camera.activeTexture, material); // カメラにマテリアルを適用

            context.ExecuteCommandBuffer(cmd); // コマンドバッファ実行
            context.Submit();
        }
    }

    CustomRenderPass m_ScriptablePass;

    public override void Create()
    {
        m_ScriptablePass = new CustomRenderPass();
        m_ScriptablePass.material = Resources.Load<Material>("Materials/RenderDepth"); // Resources/Materials/RenderDepth.matをロードする
        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRendering; // レンダリングが終わった後にレンダリングパスを実行
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(m_ScriptablePass);
    }
}
```


## STEP2.5 Renderer FeatureをForward Rendererアセットに登録
STEP2.1で作成したForwardRendererアセットにSTEP2.3で作成したRenderer Depth Featureを登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/8b43f56c-044f-0d6c-7cf4-b0852097c116.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/274aab9a-4100-9a8c-0bd6-d283ce1daaef.png)


## STEP2.6 レンダリングパイプライン完成
ヒエラルキーにあるCameraコンポーネント上のRendererから RenderDepthを選択すると、画面の表示が変化します。
![camera_select.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/701ff4cd-02f8-30a5-294f-7e36119efa33.gif)

レンダリングの終了タイミングでシェーダーグラフが実行され、画面に反映されるようなレンダリングパイプラインの完成です。

## STEP2.7 色を変えてみる
マテリアルのパラメータを変更すると、即座に画面にも反映されます。
![change_color.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44288/a49e6eb6-c684-87a1-de01-1bd652e939a5.gif)


# まとめ
今回、ShaderGraphをポストエフェクトとして利用するようなレンダリングパイプラインを簡単に実装することができました。

直感的にシェーダーを組めるのがShaderGraphの強みですが、
レンダリングパイプラインにShaderGraphを組み込んだことによって直感的にポストエフェクトを組める環境が作れたのではないかと感じています。

# 追記 : Unity2019.4LTS でやりたい場合
Unity2019.4 だと ```RenderPassEvent.AfterRendering+1```に ```FinalPostProcessPass``` や ```Final Blit Pass``` が入るため、
今回追加したポストプロセスPassが握りつぶされてしまいます。

これは```RenderDepthFeature.cs```の```Create```メソッドを以下のように書くことで回避できます。

```RenderDepthFeature.cs
public override void Create()
{
    m_ScriptablePass = new CustomRenderPass();
    m_ScriptablePass.material = Resources.Load<Material>("Materials/RenderDepth"); // Resources/Materials/RenderDepth.matをロードする
    m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRendering + 2; 
}
```