You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
        }
    }
}

使用步骤

  1. 在Project窗口找到你的URP Asset文件,打开Inspector面板
  2. 找到Renderer区域,点击Add Renderer Feature,选择CaptureRendererFeature
  3. 给目标相机挂载ScreenshotCapture组件,赋值_camera字段
  4. 调用TakeScreenshot()方法即可捕获带后处理效果的截图

内容的提问来源于stack exchange,提问作者Sehyup

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 20:11:03