UniversalRPで、Depthを表示するシンプルなRendererFeature

以前のQiita記事で、UniversalRPでDepth(深度)、Normal(法線)を表示するための方法を書きました。

https://qiita.com/ScreenPocket/items/26aaa14a3e524b641f46

ただ、上記記事については法線とDepthを表示しつつ切り替えたり、Depth以外を表示するための処理が色々含まれていてシンプルではなかったので、極力短いコードでDepthを表示するためのRendereFeatureを記載してみます。

環境はちょっと古めの
* Unity 2022.1.24f1
* UniversalRP13.1.9

をベースにやってみましょう

# 前提の設定
いつも通り、UniversalRPの事前準備については割愛します。
UniversalRP前提のプロジェクトから開始した想定です。

忘れずにやっておきたい点として、必ずUniversalRenderPipelineAssetのDepthTextureにチェックを入れましょう。
![スクリーンショット 2023-03-10 003333.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61266/1158bed2-3396-2d57-d47b-fbfd60c79653.png)
準備が出来たらコーディングに入ります

# コーディング

今回必要なコードは下記2点です

|コード名|備考|
|:-----------|:------------|
|DepthDisplayRendererFeature.cs|ScriptableRendererFeatureとScriptableRendererPassをまとめたコードです|
|DisplayDepth.shader|描画を行うためのBlit用シェーダです。Blitterを使た方が良いかもしれませんが、自分があまり詳しくないのでいつも通りSwapBufferのBlit()で記載します|

まずはcsファイルから書きましょう

## DepthDisplayRendererFeature.cs
コードは下記です
```cs:DepthDisplayRendererFeature.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace ScreenPocket
{
    public sealed class DepthDisplayRendererFeature : ScriptableRendererFeature
    {
        private sealed class Pass : ScriptableRenderPass
        {
            private Material _material;

            public void Setup()
            {
                renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
                profilingSampler = new ProfilingSampler("DisplayDepth Pass");

                var shaderName = "ScreenPocket/URP/DisplayDepth";
                var shader = Shader.Find(shaderName);
                if (shader == null)
                {
                    Debug.LogError($"Not found shader!{shaderName}");
                    return;
                }

                _material = CoreUtils.CreateEngineMaterial(shader);
            }

            /// <inheritdoc/>
            public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
            {
                if (_material == null)
                {
                    return;
                }

                var cmd = CommandBufferPool.Get();
                using (new ProfilingScope(cmd, profilingSampler))
                {
                    //ポイントはコレ↓ _BlitScaleBiasに値を入れておかないとBlit.hlslのVert()でuvを正しく取れない
                    _material.SetVector("_BlitScaleBias", new Vector4(1, 1, 0, 0));
                    Blit(cmd, ref renderingData, _material);
                }

                context.ExecuteCommandBuffer(cmd);
                CommandBufferPool.Release(cmd);
            }
        } //end private sealed class Pass

        private Pass _pass;

        public override void Create()
        {
            _pass = new Pass();
            _pass.Setup();
        }

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

```
シンプルですね。たったの（？）64行。
あまり語るべき場所も無いですが、やっていることは下記です

* RendererFeatureのPassを準備し、後述の「ScreenPocket/URP/DisplayDepth」シェーダを持ったマテリアルを作成
* 描画タイミングは最前面に出したいのでAfterRenderingPostProcessingで。Finalでも良いかも？（未検証）
* マテリアルのパラメータ _BlitScaleBias に値を渡して、Blit()(SwapBufferのBlit()）でスクリーンに描画

注意点としては_BlitScaleBiasに値を渡さなければならない点でしょうか。
コメントに記載した通り、上記の値を入れておかないとuvのサンプリング座標がズレてしまいます。

という事でシェーダの記載に続きます

## DisplayDepth.shader
```hlsl:DisplayDepth.shader
Shader "ScreenPocket/URP/DisplayDepth"
{
    HLSLINCLUDE
        #pragma exclude_renderers gles

        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"

        half4 Frag(Varyings input) : SV_Target
        {
#if UNITY_REVERSED_Z
            half depth = SampleSceneDepth(input.texcoord).x;
#else
            half depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(input.texcoord).x);
#endif
            return half4(depth,depth,depth,1);
        }
    ENDHLSL

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100
        ZTest Always ZWrite Off Cull Off

        Pass
        {
            Name "DisplayDepth"
        
            HLSLPROGRAM
                #pragma vertex Vert
                #pragma fragment Frag
            ENDHLSL
        }
    }
}

```
これまたシンプル、たったの39行！
やっていることは

* Blit用に"Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"をInclude(Core.hlslとCommon.hlslも必要)
* 以前に記事を書いた通り、"Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"　でDepthTextureを触る関数をInclude
* Depth(深度)を引っ張ってきて、それをそのまま表示。（UNITY_REVERSED_Zで深度値が逆の場合にも対応）

注意点としては前述した通り、頂点シェーダはBlit.hlslのVertを使っている事も有って_BlitScaleBiasが必須となっています。
詳しくはRiderでVertの中のコードを見るなり、独自に調査頂ければと思います。

# 完成

で、仕上がった画面がコチラ
<img width="300" alt="スクリーンショット 2023-03-10 005512.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61266/618bf101-a01f-50fa-4e50-3d8ae5d3d84b.png"><img width="300" alt="スクリーンショット 2023-03-10 005601.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61266/286b4285-30f7-9039-eb59-0058b21debda.png">

右がRendererFeatureを有効にした時です。
たったの　64+39=103行コードを書くだけでDepthが見られるとは・・・、UniversalRP様様ですね～

# 終わりに

という事で、フトした時に見たくなるシンプルなDepth表示機能でした。
簡易で切り替えられるようにゲーム側からのフラグを見られるようにしたり、あるいは今回のRendererFeatureとShaderを元に新しい表現に拡張していったりしても良いかもですね。
ご自由にカスタマイズください。