Skip to content

Instantly share code, notes, and snippets.

@Cyanilux
Last active February 10, 2024 19:56
Show Gist options
  • Save Cyanilux/451c84fc54aab1871fc1643751c35730 to your computer and use it in GitHub Desktop.
Save Cyanilux/451c84fc54aab1871fc1643751c35730 to your computer and use it in GitHub Desktop.
URP Renderer Feature to force a CopyDepth pass (Unity 2022.2, URP)
/*
Afaik in Unity 2022.2,
If URP uses a Depth Prepass :
- The depth texture will only contain opaque objects, based on the Default Opaque Layer Mask
at the top of the Universal Renderer asset.
- So if you remove a layer from that and instead render it with RenderObjects features,
those objects no longer appear in the depth texture, even if their shader is writing depth :(
- Same goes for any Transparent objects with ZWrite On. The depth texture won't include those
This feature can enqeue a CopyDepth pass, forcing URP to copy the depth buffer to the depth texture.
Warning : I don't know if it'll work in all cases / platforms.
If URP is not doing a prepass :
- Then the "Depth Texture Mode" on the renderer asset may already act as an alternative to this.
(But I imagine you may sometimes need to copy after opaques & again after transparents / before post process)
- Also seems using Requirement : "Depth" on a Fullscreen Renderer Feature (in Before/After Post Processing events)
moves where the regular CopyDepth pass occurs... ignoring what the Depth Texture Mode has already set
and causes flickering on any Transparent objects sampling depth. Probably a bug.
*/
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.Universal.Internal;
public class CopyDepthFeature : ScriptableRendererFeature {
private class SetGlobalTexture : ScriptableRenderPass {
private RTHandle rt;
public SetGlobalTexture(RenderPassEvent renderPassEvent) {
base.renderPassEvent = renderPassEvent;
}
public void Setup(RTHandle rt) {
this.rt = rt;
}
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { }
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
CommandBuffer cmd = CommandBufferPool.Get();
cmd.SetGlobalTexture(rt.name, rt.nameID);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
CommandBufferPool.Release(cmd);
}
}
[Tooltip("Event that the CopyDepth should occur at")]
public RenderPassEvent _event = RenderPassEvent.AfterRenderingTransparents;
[Tooltip("Assign the Renderer asset here, I don't know how else to obtain it")]
public UniversalRendererData rendererAsset;
[Tooltip("Can toggle this to try to match the depth texture format URP is using, might avoid allocating an extra RTHandle. Compare the target used by DepthPrepass/CopyDepth passes in Frame Debugger")]
public bool depthUsesPrepass = true;
private SetGlobalTexture setGlobalTexPass;
private CopyDepthPass copyDepthPass;
private Material m_CopyDepthMaterial;
private RTHandle depthTexture;
#if UNITY_SWITCH || UNITY_ANDROID
const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D24_UNorm_S8_UInt;
const int k_DepthBufferBits = 24;
#else
const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt;
const int k_DepthBufferBits = 32;
#endif
public override void Create() {
if (m_CopyDepthMaterial == null && rendererAsset != null)
m_CopyDepthMaterial = CoreUtils.CreateEngineMaterial(rendererAsset.shaders.copyDepthPS);
setGlobalTexPass = new SetGlobalTexture(_event);
copyDepthPass = new CopyDepthPass(_event, m_CopyDepthMaterial);
}
protected override void Dispose(bool disposing) {
if (m_CopyDepthMaterial != null) CoreUtils.Destroy(m_CopyDepthMaterial);
}
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData) {
// Allocate Depth Texture
// based on UniversalRenderer.cs (2022.2/staging)
// https://github.com/Unity-Technologies/Graphics/blob/866e82896d52796070d57f893e2ea1c4c56d8b95/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs#L788
var depthDescriptor = renderingData.cameraData.cameraTargetDescriptor;
if (depthUsesPrepass) {
depthDescriptor.graphicsFormat = GraphicsFormat.None;
depthDescriptor.depthStencilFormat = k_DepthStencilFormat;
depthDescriptor.depthBufferBits = k_DepthBufferBits;
} else {
depthDescriptor.graphicsFormat = GraphicsFormat.R32_SFloat;
depthDescriptor.depthStencilFormat = GraphicsFormat.None;
depthDescriptor.depthBufferBits = 0;
}
depthDescriptor.msaaSamples = 1;// Depth-Only pass don't use MSAA
RenderingUtils.ReAllocateIfNeeded(ref depthTexture, depthDescriptor, FilterMode.Point, wrapMode: TextureWrapMode.Clamp, name: "_CameraDepthTexture");
setGlobalTexPass.Setup(depthTexture);
copyDepthPass.Setup(renderer.cameraDepthTargetHandle, depthTexture);
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {
if (renderingData.cameraData.isPreviewCamera) return;
if (m_CopyDepthMaterial == null) return;
renderer.EnqueuePass(setGlobalTexPass);
renderer.EnqueuePass(copyDepthPass);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment