VR4Medical/ICI/Library/PackageCache/com.unity.render-pipelines.universal@2b88762731f8/Samples~/URPPackageSamples/RendererFeatures/KeepFrame/KeepFrameFeature.cs
2025-07-29 13:45:50 +03:00

234 lines
10 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);
}
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
{
// This path is not taken when using render graph.
// The code to reallocate m_OldFrameHandle has been moved to AddRenderPasses in order to avoid duplication.
}
protected override void Dispose(bool disposing)
{
m_OldFrameHandle?.Release();
}
}