using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class OutlineRenderFeature : ScriptableRendererFeature { class OutlinePass : ScriptableRenderPass { private RTHandle tempColorTexture; private Material outlineMaterial; private RTHandle cameraTarget; public OutlinePass(Material material) { outlineMaterial = material; } public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor; descriptor.depthBufferBits = 0; // No need for depth buffer tempColorTexture = RTHandles.Alloc(descriptor, name: "_TemporaryColorTexture"); ConfigureTarget(tempColorTexture); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (outlineMaterial == null) return; CommandBuffer cmd = CommandBufferPool.Get("Outline Pass"); cameraTarget = renderingData.cameraData.renderer.cameraColorTargetHandle; // ✅ Safe reference to camera color target // Copy to temporary texture Blit(cmd, cameraTarget, tempColorTexture); // Apply outline effect and blit back Blit(cmd, tempColorTexture, cameraTarget, outlineMaterial); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void OnCameraCleanup(CommandBuffer cmd) { if (tempColorTexture != null) { RTHandles.Release(tempColorTexture); tempColorTexture = null; } } } [SerializeField] private Material outlineMaterial; private OutlinePass outlinePass; public override void Create() { outlinePass = new OutlinePass(outlineMaterial) { renderPassEvent = RenderPassEvent.AfterRenderingOpaques // ✅ Executes after opaque objects }; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (outlinePass != null) { renderer.EnqueuePass(outlinePass); } } }