using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; /* * Blit Renderer Feature https://github.com/Cyanilux/URP_BlitRenderFeature * ------------------------------------------------------------------------------------------------------------------------ * Based on the Blit from the UniversalRenderingExamples * https://github.com/Unity-Technologies/UniversalRenderingExamples/tree/master/Assets/Scripts/Runtime/RenderPasses * * Extended to allow for : * - Specific access to selecting a source and destination (via current camera's color / texture id / render texture object * - (Pre-2021.2/v12) Automatic switching to using _AfterPostProcessTexture for After Rendering event, in order to correctly handle the blit after post processing is applied * - Setting a _InverseView matrix (cameraToWorldMatrix), for shaders that might need it to handle calculations from screen space to world. * e.g. Reconstruct world pos from depth : https://www.cyanilux.com/tutorials/depth/#blit-perspective * - (2020.2/v10 +) Enabling generation of DepthNormals (_CameraNormalsTexture) * This will only include shaders who have a DepthNormals pass (mostly Lit Shaders / Graphs) (workaround for Unlit Shaders / Graphs: https://gist.github.com/Cyanilux/be5a796cf6ddb20f20a586b94be93f2b) * ------------------------------------------------------------------------------------------------------------------------ * @Cyanilux */ namespace Cyan { /* CreateAssetMenu here allows creating the ScriptableObject without being attached to a Renderer Asset Can then Enqueue the pass manually via https://gist.github.com/Cyanilux/8fb3353529887e4184159841b8cad208 as a workaround for 2D Renderer not supporting features (prior to 2021.2). Uncomment if needed. */ // [CreateAssetMenu(menuName = "Cyan/Blit")] public class Blit : ScriptableRendererFeature { public class BlitPass : ScriptableRenderPass { public Material blitMaterial = null; public FilterMode filterMode { get; set; } private BlitSettings settings; private RenderTargetIdentifier source { get; set; } private RenderTargetIdentifier destination { get; set; } RenderTargetHandle m_TemporaryColorTexture; RenderTargetHandle m_DestinationTexture; string m_ProfilerTag; #if !UNITY_2020_2_OR_NEWER // v8 private ScriptableRenderer renderer; #endif public BlitPass(RenderPassEvent renderPassEvent, BlitSettings settings, string tag) { this.renderPassEvent = renderPassEvent; this.settings = settings; blitMaterial = settings.blitMaterial; m_ProfilerTag = tag; m_TemporaryColorTexture.Init("_TemporaryColorTexture"); if (settings.dstType == Target.TextureID) { m_DestinationTexture.Init(settings.dstTextureId); } } public void Setup(ScriptableRenderer renderer) { #if UNITY_2020_2_OR_NEWER // v10+ if (settings.requireDepthNormals) ConfigureInput(ScriptableRenderPassInput.Normal); #else // v8 this.renderer = renderer; #endif } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag); RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor; opaqueDesc.depthBufferBits = 0; // Set Source / Destination #if UNITY_2020_2_OR_NEWER // v10+ var renderer = renderingData.cameraData.renderer; #else // v8 // For older versions, cameraData.renderer is internal so can't be accessed. Will pass it through from AddRenderPasses instead var renderer = this.renderer; #endif // note : Seems this has to be done in here rather than in AddRenderPasses to work correctly in 2021.2+ if (settings.srcType == Target.CameraColor) { source = renderer.cameraColorTarget; } else if (settings.srcType == Target.TextureID) { source = new RenderTargetIdentifier(settings.srcTextureId); } else if (settings.srcType == Target.RenderTextureObject) { source = new RenderTargetIdentifier(settings.srcTextureObject); } if (settings.dstType == Target.CameraColor) { destination = renderer.cameraColorTarget; } else if (settings.dstType == Target.TextureID) { destination = new RenderTargetIdentifier(settings.dstTextureId); } else if (settings.dstType == Target.RenderTextureObject) { destination = new RenderTargetIdentifier(settings.dstTextureObject); } if (settings.setInverseViewMatrix) { Shader.SetGlobalMatrix("_InverseView", renderingData.cameraData.camera.cameraToWorldMatrix); } if (settings.dstType == Target.TextureID) { if (settings.overrideGraphicsFormat) { opaqueDesc.graphicsFormat = settings.graphicsFormat; } cmd.GetTemporaryRT(m_DestinationTexture.id, opaqueDesc, filterMode); } //Debug.Log($"src = {source}, dst = {destination} "); // Can't read and write to same color target, use a TemporaryRT if (source == destination || (settings.srcType == settings.dstType && settings.srcType == Target.CameraColor)) { cmd.GetTemporaryRT(m_TemporaryColorTexture.id, opaqueDesc, filterMode); Blit(cmd, source, m_TemporaryColorTexture.Identifier(), blitMaterial, settings.blitMaterialPassIndex); Blit(cmd, m_TemporaryColorTexture.Identifier(), destination); } else { Blit(cmd, source, destination, blitMaterial, settings.blitMaterialPassIndex); } context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void FrameCleanup(CommandBuffer cmd) { if (settings.dstType == Target.TextureID) { cmd.ReleaseTemporaryRT(m_DestinationTexture.id); } if (source == destination || (settings.srcType == settings.dstType && settings.srcType == Target.CameraColor)) { cmd.ReleaseTemporaryRT(m_TemporaryColorTexture.id); } } } [System.Serializable] public class BlitSettings { public RenderPassEvent Event = RenderPassEvent.AfterRenderingOpaques; public Material blitMaterial = null; public int blitMaterialPassIndex = 0; public bool setInverseViewMatrix = false; public bool requireDepthNormals = false; public Target srcType = Target.CameraColor; public string srcTextureId = "_CameraColorTexture"; public RenderTexture srcTextureObject; public Target dstType = Target.CameraColor; public string dstTextureId = "_BlitPassTexture"; public RenderTexture dstTextureObject; public bool overrideGraphicsFormat = false; public UnityEngine.Experimental.Rendering.GraphicsFormat graphicsFormat; } public enum Target { CameraColor, TextureID, RenderTextureObject } public BlitSettings settings = new BlitSettings(); public BlitPass blitPass; public override void Create() { var passIndex = settings.blitMaterial != null ? settings.blitMaterial.passCount - 1 : 1; settings.blitMaterialPassIndex = Mathf.Clamp(settings.blitMaterialPassIndex, -1, passIndex); blitPass = new BlitPass(settings.Event, settings, name); #if !UNITY_2021_2_OR_NEWER if (settings.Event == RenderPassEvent.AfterRenderingPostProcessing) { Debug.LogWarning("Note that the \"After Rendering Post Processing\"'s Color target doesn't seem to work? (or might work, but doesn't contain the post processing) :( -- Use \"After Rendering\" instead!"); } #endif if (settings.graphicsFormat == UnityEngine.Experimental.Rendering.GraphicsFormat.None) { settings.graphicsFormat = SystemInfo.GetGraphicsFormat(UnityEngine.Experimental.Rendering.DefaultFormat.LDR); } } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (settings.blitMaterial == null) { Debug.LogWarningFormat("Missing Blit Material. {0} blit pass will not execute. Check for missing reference in the assigned renderer.", GetType().Name); return; } #if !UNITY_2021_2_OR_NEWER // AfterRenderingPostProcessing event is fixed in 2021.2+ so this workaround is no longer required if (settings.Event == RenderPassEvent.AfterRenderingPostProcessing) { } else if (settings.Event == RenderPassEvent.AfterRendering && renderingData.postProcessingEnabled) { // If event is AfterRendering, and src/dst is using CameraColor, switch to _AfterPostProcessTexture instead. if (settings.srcType == Target.CameraColor) { settings.srcType = Target.TextureID; settings.srcTextureId = "_AfterPostProcessTexture"; } if (settings.dstType == Target.CameraColor) { settings.dstType = Target.TextureID; settings.dstTextureId = "_AfterPostProcessTexture"; } } else { // If src/dst is using _AfterPostProcessTexture, switch back to CameraColor if (settings.srcType == Target.TextureID && settings.srcTextureId == "_AfterPostProcessTexture") { settings.srcType = Target.CameraColor; settings.srcTextureId = ""; } if (settings.dstType == Target.TextureID && settings.dstTextureId == "_AfterPostProcessTexture") { settings.dstType = Target.CameraColor; settings.dstTextureId = ""; } } #endif blitPass.Setup(renderer); renderer.EnqueuePass(blitPass); } } }