URPでUEのようにCustomDepthを出力する

備忘録です。
Unityは2022.3.8f1、
URPは14を使用しています。

# 結論
**カスタム深度コード**
```c#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Experimental.Rendering;

public class CustomDepthRendererFeature : ScriptableRendererFeature
{
    private CustomDepthPass m_pass;

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        // 参考：FullScreenPassRendererFeature.cs
        m_pass.Setup(renderingData);
        renderer.EnqueuePass(m_pass);
    }

    public override void Create()
    {
        m_pass = new CustomDepthPass();
    }

    protected override void Dispose(bool disposing)
    {
        // 解放処理
        base.Dispose(disposing);
        m_pass.Dispose();
    }
}

public class CustomDepthPass : ScriptableRenderPass
{
    // 実行するLightMode
    private ShaderTagId m_ShaderTagId = new ShaderTagId("CustomDepth");
    // カスタム深度テクスチャ
    private RTHandle m_CustomDepthRTV;

    public CustomDepthPass()
    {
        this.renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
    }

    public void Setup(RenderingData renderingData)
    {
        // テクスチャ作成
        var defDesc = renderingData.cameraData.cameraTargetDescriptor;
        // 深度を扱うためにFormatはDepth、depthBufferBitsは32を指定
        var depthDesc = new RenderTextureDescriptor(defDesc.width, defDesc.height, RenderTextureFormat.Depth, 32);
        depthDesc.msaaSamples = 1;
        // _CustomDepthTextureがテクスチャの名前となる
        RenderingUtils.ReAllocateIfNeeded(ref m_CustomDepthRTV, depthDesc, FilterMode.Point, TextureWrapMode.Clamp, name: "_CustomDepthTexture");
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    {
        // 参考：DepthNormalOnlyPass.cs
        base.OnCameraSetup(cmd, ref renderingData);
        ConfigureTarget(m_CustomDepthRTV);
        ConfigureClear(ClearFlag.All, Color.black);
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        var cmd = CommandBufferPool.Get();
        using (new ProfilingScope(cmd, new ProfilingSampler("Custom Depth Pass")))
        {
            context.ExecuteCommandBuffer(cmd);
            cmd.Clear();
            FilteringSettings filteringSettings = FilteringSettings.defaultValue;
            DrawingSettings drawSetting = CreateDrawingSettings(m_ShaderTagId, ref renderingData, SortingCriteria.CommonTransparent);
            context.DrawRenderers(renderingData.cullResults, ref drawSetting, ref filteringSettings);
        }
        // グローバルテクスチャとしてカスタム深度テクスチャを設定
        cmd.SetGlobalTexture("_CustomDepthTexture", m_CustomDepthRTV);
        context.ExecuteCommandBuffer(cmd);
        cmd.Clear();
        CommandBufferPool.Release(cmd);
    }

    public void Dispose()
    {
        // テクスチャ解放
        RTHandles.Release(m_CustomDepthRTV);
        m_CustomDepthRTV = null;
    }
}
```
**カスタム深度シェーダー**
```hlsl
Shader "Custom/CustomDepth"
{
	Properties
	{ }

	SubShader
	{
		Tags { "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" }

		Pass
		{
			HLSLPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"

			struct Attributes
			{
				float4 positionOS   : POSITION;
			};

			struct Varyings
			{
				float4 positionHCS  : SV_POSITION;
			};

			Varyings vert(Attributes IN)
			{
				Varyings OUT;
				OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
				return OUT;
			}

			half4 frag() : SV_Target
			{
				return half4(0, 0, 1, 1);
			}
			ENDHLSL
		}

		Pass
		{
			Name "CustomDepthPass"
			Tags
			{
				"LightMode" = "CustomDepth"
			}

			// -------------------------------------
			// Render State Commands
			ZWrite On
			ColorMask R

			HLSLPROGRAM
			#pragma target 2.0

			// -------------------------------------
			// Shader Stages
			#pragma vertex DepthOnlyVertex
			#pragma fragment DepthOnlyFragment

			// -------------------------------------
			// Material Keywords
			#pragma shader_feature_local_fragment _ALPHATEST_ON
			#pragma shader_feature_local_fragment _GLOSSINESS_FROM_BASE_ALPHA

			// -------------------------------------
			// Unity defined keywords
			#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE

			//--------------------------------------
			// GPU Instancing
			#pragma multi_compile_instancing
			#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"

			// -------------------------------------
			// Includes
			#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
			#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
			ENDHLSL
		}
	}
}
```
上記のコードとシェーダーを作成して、シェーダー（マテリアル）をオブジェクトに設定。

さらに、UniversalRendererDataからAddRendererFeatureを選択し、以下のように作成したCustomDepthRendererFeatureを追加すればカスタム深度の出力が可能になります。
![customdepth_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3557263/e587e247-cd30-4b86-96d3-9192ef70f72f.png)

左側の青のオブジェクトがカスタム深度シェーダーを割り当てたオブジェクトです。
![customdepth_game.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3557263/71f433f2-eba5-efc4-6e1c-decc07931793.png)

FrameDebuggerでCustomDepthPassを確認してみると左側のオブジェクトの深度のみが無事出力されています。
![customdepth_depth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3557263/361ee6aa-58ae-fe31-0a28-c94b3479d1dc.png)

# 解説
基本的にはコメントで説明をしていますが、補足をしていきます。
また、各種処理の実行タイミングなどは憶測も入っていますのでご注意ください。

### コメントに記載している // 参考：~.cs について
これは各種処理の実行のタイミングを、記載した標準のURPのコードを参考しているという意味です。

参考にさせていただいたコードによってこれらの実行のタイミングがバラバラになっている事があったので、ひとまず実行のタイミングは標準のURPに合わせました。

### 描画のタイミングについて
renderPassEventをAfterRenderingTransparentsに設定しているため、DrawSettingsのSortingCriteriaにはCommonTransparentを設定しています。不透明オブジェクトしかカスタム深度として出力しない場合にはこれらをOpaqueで設定すれば良いと思います。

### シェーダーのCustomDepthPassについて
ここでは深度の出力を行っています。
LightModeにCustomDepthを指定することで、作成したカスタム深度コードからパスが実行されます。

# カスタム深度テクスチャの使用方法について
シェーダーの変数宣言で、以下のように宣言すればテクスチャを取得できます。
```hlsl
TEXTURE2D(_CustomDepthTexture);
SAMPLER(sampler_CustomDepthTexture);
```

# 参考にさせていただいた記事など
https://qiita.com/r-ngtm/items/6ce8ba371d4f2be6e559
https://zenn.dev/r_ngtm/articles/unity-mrt-urp10-urp14