Unity URP下RenderTexture截图丢失后处理效果的技术问询
URP下基于Scriptable RenderPass实现带后处理的运行时截图方案
问题根源
直接通过设置相机targetTexture并调用Camera.Render()的方式无法捕获后处理效果,因为URP的后处理逻辑是通过RenderPass流水线执行的,手动调用Camera.Render()只会触发基础的相机渲染流程,不会走URP完整的后处理RenderPass环节。
基于Scriptable RenderPass的解决方案思路
通过自定义RenderPass插入到URP渲染流程的后处理完成阶段,直接捕获经过所有后处理的最终渲染画面,再将结果保存为图片。
1. 自定义CaptureRenderPass(捕获后处理画面)
创建继承自ScriptableRenderPass的自定义Pass,负责将经过后处理的画面Blit到指定的RenderTexture中。
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class CapturePostProcessedPass : ScriptableRenderPass { private RenderTargetIdentifier _source; private RenderTexture _targetRT; private Material _blitMaterial; public void Setup(RenderTexture targetRT) { _targetRT = targetRT; // 使用URP内置的Blit Shader完成画面复制 _blitMaterial = new Material(Shader.Find("Hidden/Universal Render Pipeline/Blit")); } public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { // 获取经过后处理的最终颜色缓冲区 _source = renderingData.cameraData.renderer.cameraColorTarget; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (_targetRT == null || _blitMaterial == null) return; CommandBuffer cmd = CommandBufferPool.Get("CapturePostProcessedPass"); // 将后处理完成的画面复制到目标RenderTexture cmd.Blit(_source, new RenderTargetIdentifier(_targetRT), _blitMaterial, 0); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } public override void FrameCleanup(CommandBuffer cmd) { _targetRT = null; } }
2. 自定义RendererFeature(集成到URP管线)
创建ScriptableRendererFeature,将自定义Pass注册到URP的渲染流程中,指定在后处理完成后执行。
using UnityEngine; using UnityEngine.Rendering.Universal; public class CaptureRendererFeature : ScriptableRendererFeature { private CapturePostProcessedPass _capturePass; // 静态变量传递目标RenderTexture public static RenderTexture CaptureTarget { get; set; } public override void Create() { _capturePass = new CapturePostProcessedPass(); // 设置Pass执行时机:所有后处理渲染完成后 _capturePass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { // 只有当存在目标RT时,才将Pass加入渲染队列 if (CaptureTarget != null) { _capturePass.Setup(CaptureTarget); renderer.EnqueuePass(_capturePass); } } }
3. 截图调用逻辑
修改原有的截图代码,通过自定义RenderFeature传递目标RenderTexture,等待渲染完成后读取像素并保存。
using System.IO; using UnityEngine; using UnityEngine.Rendering.Universal; using Cysharp.Threading.Tasks; public class ScreenshotCapture : MonoBehaviour { [SerializeField] private Camera _camera; private readonly string FolderPath = Application.persistentDataPath + "/Screenshots"; private string TotalPath => FolderPath + "/screenshot_" + System.DateTime.Now.ToString("yyyyMMddHHmmss") + ".png"; public void TakeScreenshot() { StartCoroutine(RecordFrame()); } private System.Collections.IEnumerator RecordFrame() { TurnOffOtherCameras(); yield return new WaitForEndOfFrame(); _ = CaptureScreenAndSave(); TurnOnOtherCameras(); } private async UniTask<bool> CaptureScreenAndSave() { int pixelWidth = _camera.pixelWidth; int pixelHeight = _camera.pixelHeight; // 创建用于存储截图的RenderTexture var captureRT = new RenderTexture(pixelWidth, pixelHeight, 24, RenderTextureFormat.ARGB32); captureRT.Create(); // 传递目标RT给RenderFeature,触发下一帧的捕获逻辑 CaptureRendererFeature.CaptureTarget = captureRT; // 等待下一帧渲染完成,此时RenderPass已将带后处理的画面写入captureRT await UniTask.NextFrame(); // 读取RenderTexture中的像素 Texture2D screenTex = new Texture2D(pixelWidth, pixelHeight, TextureFormat.ARGB32, false); RenderTexture.active = captureRT; screenTex.ReadPixels(new Rect(0, 0, pixelWidth, pixelHeight), 0, 0); screenTex.Apply(); // 保存图片到本地 if (!Directory.Exists(FolderPath)) { Directory.CreateDirectory(FolderPath); } File.WriteAllBytes(TotalPath, screenTex.EncodeToPNG()); // 清理资源 RenderTexture.active = null; Destroy(screenTex); Destroy(captureRT); CaptureRendererFeature.CaptureTarget = null; return true; } private void TurnOffOtherCameras() { foreach (var cam in FindObjectsOfType<Camera>()) { if (cam != _camera) cam.enabled = false; } } private void TurnOnOtherCameras() { foreach (var cam in FindObjectsOfType<Camera>()) { if (cam != _camera) cam.enabled = true; } } }
使用步骤
- 在Project窗口找到你的URP Asset文件,打开Inspector面板
- 找到
Renderer区域,点击Add Renderer Feature,选择CaptureRendererFeature - 给目标相机挂载
ScreenshotCapture组件,赋值_camera字段 - 调用
TakeScreenshot()方法即可捕获带后处理效果的截图
内容的提问来源于stack exchange,提问作者Sehyup
相关产品推荐
相关产品推荐

