这里我们需要涉及到三个文件:
- 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;
}
}
}