rn.log

備忘録など

【URP10.8.1】ConfigureTargetでDepthバッファを指定しないとZテストが機能しない件についてメモ

はじめに

自前で用意したレンダーターゲットにオブジェクトを描画する時、デプスバッファが正しく指定されていない場合にZテストが正常に機能しません。

今回の記事では、Zテストが正常に機能しないケースの実装と、Zテストが正常に機能するケースの2つを紹介したいと思います。

環境

Universal RP 10.8.1 Unity2020.3.32f1

ScriptableRenderPassのConfigure

ScriptableRenderPassを用いてオブジェクト描画する処理を自前で実装したいケースを考えます。

ScriptableRenderPassを継承したクラスでは、ConfigureTargetメソッドを利用してレンダーターゲットを切り替えることができます。 ConfigureTargetではカラーバッファのほかに、デプスバッファを指定することができます。

            ConfigureTarget(_colorTargetId); // カラーだけを指定
            ConfigureTarget(_colorTargetId, _depthTargetId); // カラーとデプスの両方を指定

検証1

以下のようにオブジェクトを配置してみました。

通常の描画

より手前のオブジェクトが画面に表示されます。 Zテストが正常に機能していることが分かります。

使用したシェーダー

シェーダー1

手前の白いオブジェクトには、以下のシェーダーをアタッチしています

Shader "Unlit/1"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags { "LightMode"="SRPDefaultUnlit" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            half4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

シェーダー2

奥の赤いオブジェクトには、以下のシェーダーをアタッチしています

Shader "Unlit/2"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags { "LightMode"="SRPDefaultUnlit" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            half4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

検証2. 片方のオブジェクトを別のカラーバッファに書きこみ

シェーダー書き換え

シェーダー2のタグを以下のように変更します。

Tags { "LightMode"="MyTag" }

URPはMyTagタグを実行してはくれません。 実行させるためのレンダラー実行処理を記述する必要があります。

ScriptableRenderPass

オブジェクトを描画するScriptableRenderPassをまず作成します。 このRenderPassは任意のレンダーターゲットの指定が可能です。

URP内部のRenderObjectsPassを参考にして実装しました。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;

namespace MyRendering
{
    public class MyRenderObjectsPass : ScriptableRenderPass
    {
        RenderQueueType renderQueueType;
        FilteringSettings m_FilteringSettings;
        ProfilingSampler m_ProfilingSampler;

        public Material overrideMaterial { get; set; }
        public int overrideMaterialPassIndex { get; set; }

        List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
        RenderStateBlock m_RenderStateBlock;
        private int _colorTargetId;

        public void Setup(int colorTargetId)
        {
            _colorTargetId = colorTargetId;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            base.Configure(cmd, cameraTextureDescriptor);
            cmd.GetTemporaryRT(_colorTargetId, cameraTextureDescriptor);
            
            ConfigureTarget(_colorTargetId);
        }

        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            base.OnCameraCleanup(cmd);
            cmd.ReleaseTemporaryRT(_colorTargetId);
        }

        public MyRenderObjectsPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask)
        {
            base.profilingSampler = new ProfilingSampler(nameof(MyRenderObjectsPass));

            m_ProfilingSampler = new ProfilingSampler(profilerTag);
            this.renderPassEvent = renderPassEvent;
            this.renderQueueType = renderQueueType;
            this.overrideMaterial = null;
            this.overrideMaterialPassIndex = 0;
            RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent)
                ? RenderQueueRange.transparent
                : RenderQueueRange.opaque;
            m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);

            if (shaderTags != null && shaderTags.Length > 0)
            {
                foreach (var passName in shaderTags)
                    m_ShaderTagIdList.Add(new ShaderTagId(passName));
            }
            else
            {
                m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
                m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
                m_ShaderTagIdList.Add(new ShaderTagId("UniversalForwardOnly"));
                m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));
            }

            m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            SortingCriteria sortingCriteria = (renderQueueType == RenderQueueType.Transparent)
                ? SortingCriteria.CommonTransparent
                : renderingData.cameraData.defaultOpaqueSortFlags;

            DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortingCriteria);
            drawingSettings.overrideMaterial = overrideMaterial;
            drawingSettings.overrideMaterialPassIndex = overrideMaterialPassIndex;

            // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
            // Currently there's an issue which results in mismatched markers.
            CommandBuffer cmd = CommandBufferPool.Get();
            using (new ProfilingScope(cmd, m_ProfilingSampler))
            {
                context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings,
                    ref m_RenderStateBlock);
            }
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
    }
}

ScriptableRendererFeature

次に、 MyTagパスタグが付いたシェーダーを実行し、_MyColorTarget というレンダーターゲットに対して描画を行うScriptableRendererFeatureを作成します。

URP内部のRenderObjectsを参考にして実装しました。

using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;

namespace MyRendering
{
    public class MyRenderObjects : ScriptableRendererFeature
    {
        MyRenderObjectsPass renderObjectsPass;

        public override void Create()
        {
            var lightModeTags = new string[]
            {
                "MyTag"
            };
            renderObjectsPass = new MyRenderObjectsPass(
                "Render MyTag",
                RenderPassEvent.AfterRendering,
                lightModeTags,
                RenderQueueType.Opaque,
                -1);
        }

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            int colorTargetId = Shader.PropertyToID("_MyColorTarget");
            renderObjectsPass.Setup(colorTargetId);

            renderer.EnqueuePass(renderObjectsPass);
        }
    }
}

上記のRendererFeatureを Forward Renderer Data に登録すると、RendererFeatureが実行されます。

結果

FrameDebuggerを見ると、シェーダー2がアタッチされた赤いオブジェクトが、_MyColorTarget というレンダーターゲットに描画されることが確認できます。 検証1では赤いオブジェクトはZテストによって白いオブジェクトの裏に隠れていましたが、検証2では隠れていません。

検証3. デプスバッファを指定する

MyRenderObjectsPassにてデプスバッファを指定できるようにします。 MyRenderObjectsPass.cs を以下のように書き換えます。

        public void Setup(int colorTargetId, int depthTargetId)
        {
            _colorTargetId = colorTargetId;
            _depthTargetId = depthTargetId;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            base.Configure(cmd, cameraTextureDescriptor);
            cmd.GetTemporaryRT(_colorTargetId, cameraTextureDescriptor);
            
            ConfigureTarget(_colorTargetId, _depthTargetId);
        }

MyRenderObjects.cs は、以下のように書き換えます。 _CameraDepthTexture をレンダーターゲットのデプスバッファに指定しています。

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            int colorTargetId = Shader.PropertyToID("_MyColorTarget");
            int depthTargetId = Shader.PropertyToID("_CameraDepthTexture");
            renderObjectsPass.Setup(colorTargetId, depthTargetId);

            renderer.EnqueuePass(renderObjectsPass);
        }

_CameraDepthTexture を有効化するため、Universal Render Pipeline Asset の Depth Texture を有効化しておきます。

結果

赤いオブジェクトが、手前のオブジェクトに遮蔽されるようになりました。

その他 : _CameraDepthTexture が書きこまれるパス

CopyDepth というパスが実行されたタイミングで、_CameraDepthTexture へデプス値が書きこまれます。