使用RenderGraph实现截图


这里我们需要涉及到三个文件:

  • ScreenCaptureFeature:继承自ScriptableRendererFeature,用于添加到UniversalRendererData中,来给对应的ScriptableRenderer添加ScreenCapturePass。
  • ScreenCapturePass:继承自ScriptableRenderPass,用于实现具体的截图流程。
  • ScreenCaptureHelper:提供截图功能的入口函数
public class ScreenCaptureFeature : ScriptableRendererFeature
{
    private ScreenCapturePass m_ScreenCapturePass;
    private Camera _targetCamera;
    private Action<Texture2D> _callback;

    public override void Create()
    {
        m_ScreenCapturePass = new ScreenCapturePass();
        m_ScreenCapturePass.renderPassEvent = RenderPassEvent.AfterRendering; // 设置该Pass执行时间为所有渲染之后(FinalBlit之前)

        ScreenCaptureHelper.Instance.registerFeature(this); // 向ScreenCaptureHelper注册该ScreenCaptureFeature实例
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.camera == _targetCamera) // 只有指定的摄像机才添加截图Pass
        {
            _targetCamera = null;
            renderer.EnqueuePass(m_ScreenCapturePass);
            m_ScreenCapturePass.requestCapture(onCaptureOver);
        }
    }

    public void requestCapture(Camera camera, Action<Texture2D> callback)
    {
        _targetCamera = camera;
        _callback = callback;
    }

    private void onCaptureOver(Texture2D texture)
    {
        if (_callback != null)
        {
            _callback.Invoke(texture);
            _callback = null;
        }
        else
        {
#if UNITY_EDITOR
            Object.DestroyImmediate(texture);
#else
                Object.Destroy(texture);
#endif
        }
    }
}
public class ScreenCapturePass : ScriptableRenderPass
{
    private class BlitPassData // 拷贝Pass的参数
    {
        public TextureHandle source; // 传递给Blitter的源
    }

    private class ReadbackPassData // GPU Readback Pass的参数
    {
    }

    private Action<Texture2D> _captureCallback; // 结果回调

    public void requestCapture(Action<Texture2D> callback)
    {
        _captureCallback = callback;
    }

    // RenderGraph 入口
    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
    {
        TextureHandle destination;
        // 添加屏幕拷贝Pass
        using (var builder = renderGraph.AddRasterRenderPass<BlitPassData>("Screen Capture Pass", out var passData))
        {
            UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
            passData.source = resourceData.cameraColor; // 取当前的CameraColor作为拷贝的源

            TextureDesc desc = new TextureDesc(Vector2.one) // 使用屏幕尺寸1比1作为目标尺寸
            {
                colorFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.R8G8B8A8_SRGB, // 设置纹理格式,注意这里如果是非SRGB,那么后面取回时会颜色失真
                name = "CaptureTexture"
            };
            destination = renderGraph.CreateTexture(desc); // 创建目标纹理

            builder.UseTexture(passData.source, AccessFlags.Read); // 告诉RenderGraph这个Pass要用到CameraColor
            builder.SetRenderAttachment(destination, 0); // 设置目标纹理
            builder.AllowPassCulling(false); // 禁止Pass裁剪(如果RenderGraph检测到某个Pass的产出在之后不会被用到的话,会自动裁剪掉该Pass)

            // 设置该Pass的具体工作内容
            builder.SetRenderFunc((BlitPassData data, RasterGraphContext context) =>
            {
                Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), 0, false); // 将data.source(即CameraColor)内容copy到渲染目标
            });
        }

        // 纹理取回Pass
        using (var builder = renderGraph.AddComputePass<ReadbackPassData>("Readback Pass", out var passData))
        {
            builder.UseTexture(destination); // 告诉RenderGraph该Pass需要用到之前的渲染目标
            builder.AllowPassCulling(false); // 阻止该Pass被裁剪
            builder.SetRenderFunc((ReadbackPassData data, ComputeGraphContext context) =>
            {
                AsyncGPUReadback.Request(destination, 0, OnReadbackComplete); // 异步取回渲染纹理的内容
            });
        }
    }

    private void OnReadbackComplete(AsyncGPUReadbackRequest request)
    {
        // 检查是否有错误
        if (request.hasError)
        {
            Debug.LogError("GPU Readback failed");
            _captureCallback?.Invoke(null);
            _captureCallback = null;
            return;
        }

        var data = request.GetData<uint>();

        if (_captureCallback != null)
        {
            // 保存为Texture2D
            Texture2D screenshot = new Texture2D(
                request.width,
                request.height,
                TextureFormat.RGBA32,
                false);

            screenshot.LoadRawTextureData(data);
            screenshot.Apply();

            _captureCallback(screenshot);
            _captureCallback = null;
        }
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注