【Unity】テクスチャ情報を NativeArray として取得できる Texture2D.GetPixelData() の使い方

Unity でテクスチャの色を Jobs/Burst で処理できる NativeArray の形で取得できる Texture2D.GetPixelData の使い方をまとめます。

Unity 2021.3.0f1

はじめに

Unity2020.1 から Texture2D.GetPixelData() というAPIが新しく追加されました。

このメソッドは Texture2D.GetPixels() のように Color 型の配列を返すのではなく、NativeArray<Color> を返します。
つまり、テクスチャの色をC#で頻繁に変更するようなケースにおいて、Job システム や Burst コンパイラによる高速化を行うことができます。

本記事ではこの API の使い方についてまとめます。

使い方

以下は Job システムなどと組み合わせていない、この API の簡単な使い方です。

using UnityEngine;
using UnityEngine.UI;

public sealed class Example : MonoBehaviour
{
    [SerializeField] private Vector3 _colorSeed;
    [SerializeField] private RawImage _rawImage;
    private Texture2D _texture;

    private void Update()
    {
        // テクスチャの色情報をNativeArrayで取得
        var pixelData = _texture.GetPixelData<Color32>(0);

        // 色を適当に変更する
        var seeds = _colorSeed + Vector3.one * Time.time;
        for (var i = 0; i < pixelData.Length; i++)
        {
            var p = (float)i / pixelData.Length;
            pixelData[i] = GetColor(p, seeds);
        }

        // GPUにアップロード
        _texture.Apply();
    }

    public void OnEnable()
    {
        var tex = new Texture2D(128, 128, TextureFormat.RGBA32, false);
        _rawImage.texture = tex;
        _texture = tex;
    }

    private void OnDisable()
    {
        Destroy(_texture);
    }

    // 適当に色を作って返すメソッド
    private static Color GetColor(float p, Vector3 s)
    {
        var r = 0.5f + 0.5f * Mathf.Cos(Mathf.PI * 2 * (p + s.x));
        var g = 0.5f + 0.5f * Mathf.Cos(Mathf.PI * 2 * (p + s.y));
        var b = 0.5f + 0.5f * Mathf.Cos(Mathf.PI * 2 * (p + s.z));
        return new Color(r, g, b);
    }
}

説明はコメントの通りです。

結果は以下のようになります(実際にはアニメーションします)。
Color Seed プロパティを変更することでグラデーションのかかり方が変わります。

結果

Jobシステム・Burstコンパイラと組み合わせたサンプル

さて実際にはこれを Job システムによりマルチスレッド化したり、Burst コンパイラにより高速化したりします。
このサンプルと処理速度の比較については、以下の公式リポジトリがわかりやすいです。

github.com

リポジトリの内容としては、下図のようなプロシージャルテクスチャを毎フレーム更新するものになります(実際にはアニメーションします)。

リポジトリの内容

以下の4パターンで処理を分け、処理速度が比較されているので、その結果を以下に転載します。
(計測環境は 2019 MacBookPro(Core i9 2.4GHz, 8 cores / 16 threads) とのことです。)

処理 速度
Texture2D.SetPixel() を使ってシングルスレッドで処理(Texture2D.GetPixelData() は使わない) 140ms
Texture2D.SetPixels() を使ってシングルスレッドで処理(Texture2D.GetPixelData() は使わない) 113ms
Texture2D.GetPixelData() でNativeArray を取得して Burst コンパイラを適用 17ms
Texture2D.GetPixelData() でNativeArray を取得して Job システムと Burst コンパイラを適用 1.7ms

Job システムによるマルチスレッド化と Burst コンパイラの効果が明確に表れていることが確認できます。

参考

docs.unity3d.com

github.com