【Unity】GPU上のRenderTextureのピクセル情報を非同期的に取得するAsyncGPUReadback

UnityでGPU上のRenderTextureのピクセル情報を非同期的に取得するAsyncGPUReadbackについてまとめました。

Unity2019.4.0

RenderTextureのピクセル情報を取得する方法と問題点

いま、RenderTextureからピクセル情報を取得することを考えます。
RenderTextureのデータは少し特殊でGPU上にしか存在しないため、ちょっと回りくどいことをする必要があります。

まず同期的に読み込むには以下の記事のようにRenderTexture.activeに設定したRenderTextureの情報をTexture2D.ReadPixels()で読むという方法を使います。

light11.hatenadiary.com

ただしこの方法は処理負荷が高いため、データサイズによっては一瞬画面が固まってしまったりします。

AsyncGPUReadbackで非同期的に取得

そこでAsyncGPUReadbackを使うとRenderTextureの情報を非同期的に取得することができます。
AsyncGPUReadbackは以下のようにして使用します。

using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Rendering;
using UnityEngine.UI;

public class AsyncGPUReadbackExample : MonoBehaviour
{
    [SerializeField] private Camera _camera;
    [SerializeField] private RawImage _ui;

    private void Start()
    {
        Assert.IsNotNull(_camera);
        Assert.IsNotNull(_ui);

        // カメラの描画結果をRenderTextureに描画する
        var rt = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.ARGB32);
        var oldTarget = _camera.targetTexture;
        _camera.targetTexture = rt;
        _camera.Render();
        _camera.targetTexture = oldTarget;
        
        // GPU上にあるピクセル情報を取得する
        AsyncGPUReadback.Request(rt, 0, request => {
            if (request.hasError)
            {
                // エラー
                Debug.LogError("Error.");
            }
            else
            {
                // データを取得してTexture2Dに反映する
                var data = request.GetData<Color32>();
                var texture = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false);
                texture.LoadRawTextureData(data);
                texture.Apply();
                
                // UIに反映
                _ui.texture = texture;
                
                // 元のRenderTextureはもういらないので解放
                RenderTexture.ReleaseTemporary(rt);
            }
        });
    }
}

使える環境について

AsyncGPUReadbackはすべてのプラットフォームで使えるわけではありません。
Windowsに対応しているのと、Unity2018.3からはMetalに対応しています。
[2021.08.17 追記] Vulkanでも使えるようです。

Keijiro Takahashi on Twitter: "Async GPU readback support for Metal was added in Unity 2018.3 beta. I updated AsyncCaptureTest example and verified that it works correctly on Mac. https://t.co/3vh8a6zvFt… https://t.co/KIUEMXjg8u"

対応しているかどうかはSystemInfoで調べられるので、マルチプラットフォーム対応する場合にはこれを使って同期処理と分岐する必要がありそうです。

docs.unity3d.com

関連

light11.hatenadiary.com

参考

docs.unity3d.com

docs.unity3d.com

Keijiro Takahashi on Twitter: "Async GPU readback support for Metal was added in Unity 2018.3 beta. I updated AsyncCaptureTest example and verified that it works correctly on Mac. https://t.co/3vh8a6zvFt… https://t.co/KIUEMXjg8u"