228 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using UnityEditor;
 | |
| using UnityEngine;
 | |
| using UnityEngine.Experimental.Rendering;
 | |
| using UnityEngine.Rendering.RenderGraphModule;
 | |
| using UnityEngine.Rendering;
 | |
| using UnityEngine.Rendering.RenderGraphModule.Util;
 | |
| using UnityEngine.Rendering.Universal;
 | |
| 
 | |
| // Create a Scriptable Renderer Feature that replicates Don't Clear behavior by injecting two render passes into the pipeline.
 | |
| // The first pass copies the camera color target at the end of a frame. The second pass draws the contents of the copied texture at the beginning of a new frame.
 | |
| // For more information about creating Scriptable Renderer Features, refer to https://docs.unity3d.com/Manual/urp/customizing-urp.html.
 | |
| public class KeepFrameFeature : ScriptableRendererFeature
 | |
| {
 | |
|     // Create the custom render pass that copies the camera color to a destination texture.
 | |
|     class CopyFramePass : ScriptableRenderPass
 | |
|     {
 | |
|         // Declare the destination texture.
 | |
|         RTHandle m_Destination;
 | |
| 
 | |
|         // Declare the resources the render pass uses.
 | |
|         public void Setup(RTHandle destination)
 | |
|         {
 | |
|             m_Destination = destination;
 | |
|         }
 | |
| 
 | |
| #pragma warning disable 618, 672 // Type or member is obsolete, Member overrides obsolete member
 | |
| 
 | |
|         // Override the Execute method to implement the rendering logic.
 | |
|         // This method is used only in the Compatibility Mode path.
 | |
|         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
 | |
|         {
 | |
|             // Skip rendering if the camera isn't a game camera.
 | |
|             if (renderingData.cameraData.camera.cameraType != CameraType.Game)
 | |
|                 return;
 | |
| 
 | |
|             // Set the source texture as the camera color target.
 | |
|             var source = renderingData.cameraData.renderer.cameraColorTargetHandle;
 | |
| 
 | |
|             // Get a command buffer.
 | |
|             CommandBuffer cmd = CommandBufferPool.Get("CopyFramePass");
 | |
| 
 | |
|             // Blit the camera color target to the destination texture.
 | |
|             Blit(cmd, source, m_Destination);
 | |
| 
 | |
|             // Execute the command buffer.
 | |
|             context.ExecuteCommandBuffer(cmd);
 | |
|             CommandBufferPool.Release(cmd);
 | |
|         }
 | |
| 
 | |
| #pragma warning restore 618, 672
 | |
| 
 | |
|         // Override the RecordRenderGraph method to implement the rendering logic.
 | |
|         // This method is used only in the render graph system path.
 | |
|         public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
 | |
|         {
 | |
|             // Get the resources the pass uses.
 | |
|             UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
 | |
|             UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
 | |
| 
 | |
|             // Skip rendering if the camera isn't a game camera.
 | |
|             if (cameraData.camera.cameraType != CameraType.Game)
 | |
|                 return;
 | |
|             
 | |
|             // Set the source texture as the camera color target.
 | |
|             TextureHandle source = resourceData.activeColorTexture;
 | |
|             
 | |
|             // Import the texture that persists across frames, so the render graph system can use it.
 | |
|             TextureHandle destination = renderGraph.ImportTexture(m_Destination);
 | |
|             
 | |
|             if (!source.IsValid() || !destination.IsValid())
 | |
|                 return;
 | |
|             
 | |
|             // Blit the content of the copied texture to the camera color target with a material.
 | |
|             RenderGraphUtils.BlitMaterialParameters para = new(source, destination, Blitter.GetBlitMaterial(TextureDimension.Tex2D), 0);
 | |
|             renderGraph.AddBlitPass(para, "Copy Frame Pass");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Create the custom render pass that draws the contents of the copied texture at the beginning of a new frame.
 | |
|     class DrawOldFramePass : ScriptableRenderPass
 | |
|     {
 | |
|         // Declare the resources the render pass uses.
 | |
|         class PassData
 | |
|         {
 | |
|             public TextureHandle source;
 | |
|             public Material material;
 | |
|             public string name;
 | |
|         }
 | |
| 
 | |
|         Material m_DrawOldFrameMaterial;
 | |
|         RTHandle m_Handle;
 | |
|         string m_TextureName;
 | |
| 
 | |
|         // Set up the resources the render pass uses.
 | |
|         public void Setup(Material drawOldFrameMaterial, RTHandle handle, string textureName)
 | |
|         {
 | |
|             m_DrawOldFrameMaterial = drawOldFrameMaterial;
 | |
|             m_TextureName = textureName;
 | |
|             m_Handle = handle;
 | |
|         }
 | |
| 
 | |
|         // Blit the copied texture to the camera color target.
 | |
|         // This method uses common draw commands that both the render graph system and Compatibility Mode paths can use.
 | |
|         static void ExecutePass(RasterCommandBuffer cmd, RTHandle source, Material material)
 | |
|         {
 | |
|             if (material == null)
 | |
|                 return;
 | |
| 
 | |
|             // Get the viewport scale.
 | |
|             Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
 | |
| 
 | |
|             // Blit the copied texture to the camera color target.
 | |
|             Blitter.BlitTexture(cmd, source, viewportScale, material, 0);
 | |
|         }
 | |
| 
 | |
| #pragma warning disable 618, 672 // Type or member is obsolete, Member overrides obsolete member
 | |
| 
 | |
|         // Override the Execute method to implement the rendering logic.
 | |
|         // This method is used only in the Compatibility Mode path.
 | |
|         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
 | |
|         {
 | |
|             // Get a command buffer.
 | |
|             CommandBuffer cmd = CommandBufferPool.Get(nameof(DrawOldFramePass));
 | |
|             cmd.SetGlobalTexture(m_TextureName, m_Handle);
 | |
| 
 | |
|             // Set the source texture as the camera color target.
 | |
|             var source = renderingData.cameraData.renderer.cameraColorTargetHandle;
 | |
| 
 | |
|             // Blit the camera color target to the destination texture.
 | |
|             ExecutePass(CommandBufferHelpers.GetRasterCommandBuffer(cmd), source, m_DrawOldFrameMaterial);
 | |
| 
 | |
|             // Execute the command buffer.
 | |
|             context.ExecuteCommandBuffer(cmd);
 | |
|             CommandBufferPool.Release(cmd);
 | |
|         }
 | |
| 
 | |
| #pragma warning restore 618, 672
 | |
| 
 | |
|         // Override the RecordRenderGraph method to implement the rendering logic.
 | |
|         // This method is used only in the render graph system path.
 | |
|         public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
 | |
|         {
 | |
|             // Get the resources the pass uses.
 | |
|             UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
 | |
|             UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
 | |
| 
 | |
|             // Import the texture that persists across frames, so the render graph system can use it.
 | |
|             TextureHandle oldFrameTextureHandle = renderGraph.ImportTexture(m_Handle); 
 | |
| 
 | |
|             using (var builder = renderGraph.AddRasterRenderPass<PassData>("Draw Old Frame Pass", out var passData))
 | |
|             {
 | |
|                 // Set the destination texture as the camera color target.
 | |
|                 TextureHandle destination = resourceData.activeColorTexture;
 | |
| 
 | |
|                 if (!oldFrameTextureHandle.IsValid() || !destination.IsValid())
 | |
|                     return;
 | |
| 
 | |
|                 // Set the resources the pass uses.
 | |
|                 passData.material = m_DrawOldFrameMaterial;
 | |
|                 passData.source = oldFrameTextureHandle;
 | |
|                 passData.name = m_TextureName;
 | |
| 
 | |
|                 // Set the render graph system to read the copied texture, and write to the camera color target.
 | |
|                 builder.UseTexture(oldFrameTextureHandle, AccessFlags.Read);
 | |
|                 builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
 | |
| 
 | |
|                 // Allow global state modifications. Use this only where necessary as it introduces a synchronization point in the frame, which might have an impact on performance.
 | |
|                 builder.AllowGlobalStateModification(true);
 | |
| 
 | |
|                 // Set the render method.
 | |
|                 builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
 | |
|                 {
 | |
|                     context.cmd.SetGlobalTexture(data.name, data.source);
 | |
|                     ExecutePass(context.cmd, data.source, data.material);
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     [Serializable]
 | |
|     public class Settings
 | |
|     {
 | |
|         [Tooltip("Sets the material to use to draw the previous frame.")]
 | |
|         public Material displayMaterial;
 | |
|         [Tooltip("Sets the texture to copy each frame into. The default it _FrameCopyTex.")]
 | |
|         public string textureName;
 | |
|     }
 | |
| 
 | |
|     CopyFramePass m_CopyFrame;
 | |
|     DrawOldFramePass m_DrawOldFrame;
 | |
| 
 | |
|     RTHandle m_OldFrameHandle;
 | |
| 
 | |
|     public Settings settings = new Settings();
 | |
| 
 | |
|     // In this function the passes are created and their point of injection is set
 | |
|     public override void Create()
 | |
|     {
 | |
|         m_CopyFrame = new CopyFramePass();
 | |
|         m_CopyFrame.renderPassEvent = RenderPassEvent.AfterRenderingTransparents; // Frame color is copied late in the frame
 | |
| 
 | |
|         m_DrawOldFrame = new DrawOldFramePass();
 | |
|         m_DrawOldFrame.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques; // Old frame is drawn early in the frame
 | |
|     }
 | |
| 
 | |
|     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
 | |
|     {
 | |
|         var descriptor = renderingData.cameraData.cameraTargetDescriptor;
 | |
|         descriptor.msaaSamples = 1;
 | |
|         descriptor.depthBufferBits = 0;
 | |
|         descriptor.graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB;
 | |
|         var textureName = String.IsNullOrEmpty(settings.textureName) ? "_FrameCopyTex" : settings.textureName;
 | |
|         RenderingUtils.ReAllocateHandleIfNeeded(ref m_OldFrameHandle, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: textureName);
 | |
| 
 | |
|         m_CopyFrame.Setup(m_OldFrameHandle);
 | |
|         m_DrawOldFrame.Setup(settings.displayMaterial, m_OldFrameHandle, textureName);
 | |
| 
 | |
|         renderer.EnqueuePass(m_CopyFrame);
 | |
|         renderer.EnqueuePass(m_DrawOldFrame);
 | |
|     }
 | |
| 
 | |
|     protected override void Dispose(bool disposing)
 | |
|     {
 | |
|         m_OldFrameHandle?.Release();
 | |
|     }
 | |
| }
 |