はじめに
自前で用意したレンダーターゲットにオブジェクトを描画する時、デプスバッファが正しく指定されていない場合に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 へデプス値が書きこまれます。