using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal; // Create a Scriptable Renderer Feature that either copies the depth texture to a render texture (RTHandle), or renders depth values to a render texture. // The Scriptable Renderer Feature then outputs the render texture to the screen, using a material that renders an edge effect. // For more information about creating scriptable renderer features, refer to https://docs.unity3d.com/Manual/urp/customizing-urp.html public class DepthBlitFeature : ScriptableRendererFeature { // Set the injection points for the render passes public RenderPassEvent evt_Depth = RenderPassEvent.AfterRenderingOpaques; public RenderPassEvent evt_Edge = RenderPassEvent.AfterRenderingOpaques; // Create a property for a Universal Renderer asset that sets the opaque layers to draw. public UniversalRendererData rendererDataAsset; // Create a property for the shader that copies the depth texture. public Shader copyDepthShader; // Create a property for the material that renders the edge effect. public Material m_DepthEdgeMaterial; // Set the properties of the destination depth texture. private const string k_DepthRTName = "_MyDepthTexture"; private FilterMode m_DepthRTFilterMode = FilterMode.Bilinear; private TextureWrapMode m_DepthRTWrapMode = TextureWrapMode.Clamp; // Create a class that keeps the reference to the depth texture in the frame data, so multiple passes in the render graph system can share the texture. public class TexRefData : ContextItem { public TextureHandle depthTextureHandle = TextureHandle.nullHandle; public override void Reset() { depthTextureHandle = TextureHandle.nullHandle; } } // Declare the render passes. // The script uses DepthOnlyPass for platforms that run OpenGL ES, which doesn't support copying from a depth texture. private DepthBlitCopyDepthPass m_CopyDepthPass; private DepthBlitDepthOnlyPass m_DepthOnlyPass; private DepthBlitEdgePass m_DepthEdgePass; // Check if the platform supports copying from a depth texture. private bool CanCopyDepth(ref CameraData cameraData) { bool msaaEnabledForCamera = cameraData.cameraTargetDescriptor.msaaSamples > 1; bool supportsTextureCopy = SystemInfo.copyTextureSupport != CopyTextureSupport.None; bool supportsDepthTarget = RenderingUtils.SupportsRenderTextureFormat(RenderTextureFormat.Depth); bool supportsDepthCopy = !msaaEnabledForCamera && (supportsDepthTarget || supportsTextureCopy); bool msaaDepthResolve = msaaEnabledForCamera && SystemInfo.supportsMultisampledTextures != 0; // Avoid copying MSAA depth on GLES3 platforms to avoid invalid results if (IsGLESDevice() && msaaDepthResolve) return false; return supportsDepthCopy || msaaDepthResolve; } private bool IsGLESDevice() { return SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3; } // Override the AddRenderPasses method to inject passes into the renderer. Unity calls AddRenderPasses once per camera. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { // Skip rendering if the camera is not a game camera. var cameraData = renderingData.cameraData; if (renderingData.cameraData.cameraType != CameraType.Game) return; // Set up a RenderTextureDescriptor with the properties of the depth texture. var desc = renderingData.cameraData.cameraTargetDescriptor; if (CanCopyDepth(ref cameraData)) { desc.depthBufferBits = 0; desc.msaaSamples = 1; } else { desc.graphicsFormat = GraphicsFormat.None; desc.msaaSamples = 1; } // Create the DepthBlitCopyDepthPass or DepthBlitDepthOnlyPass render pass, and inject it into the renderer. RTHandle depthRTHandle; if (CanCopyDepth(ref cameraData)) { if (m_CopyDepthPass == null) // Create a new instance of a render pass that copies the depth texture to the new render texture. // This render pass is a simplified version of CopyDepthPass in URP. m_CopyDepthPass = new DepthBlitCopyDepthPass(evt_Depth, copyDepthShader, desc, m_DepthRTFilterMode, m_DepthRTWrapMode, name: k_DepthRTName); renderer.EnqueuePass(m_CopyDepthPass); depthRTHandle = m_CopyDepthPass.depthRT; } else { if (m_DepthOnlyPass == null) // Create a new instance of a render pass that renders depth values to the new render texture. // This render pass is a simplified version of DepthOnlyPass in URP. m_DepthOnlyPass = new DepthBlitDepthOnlyPass(evt_Depth, RenderQueueRange.opaque, rendererDataAsset.opaqueLayerMask, desc, m_DepthRTFilterMode, m_DepthRTWrapMode, name: k_DepthRTName); renderer.EnqueuePass(m_DepthOnlyPass); depthRTHandle = m_DepthOnlyPass.depthRT; } // Pass the render texture to the edge effect render pass, and inject the render pass into the renderer. m_DepthEdgePass.SetRTHandle(ref depthRTHandle); renderer.EnqueuePass(m_DepthEdgePass); } public override void Create() { // Create a new instance of the render pass that renders the edge effect. m_DepthEdgePass = new DepthBlitEdgePass(m_DepthEdgeMaterial, evt_Edge); } // Free the resources the render passes use. protected override void Dispose(bool disposing) { m_CopyDepthPass?.Dispose(); m_DepthOnlyPass?.Dispose(); m_DepthEdgePass = null; m_CopyDepthPass = null; m_DepthOnlyPass = null; } }