Texture2D.GetPixelData()で直接ピクセルデータを書き換えるとアセットも変更されてしまう

Unity 2020.1から Texture2D.GetPixelData()というAPIが追加されています。 このメソッドを使えば、CPU上にあるテクスチャ色情報を直接参照することができるため、GetPixels()と比較して、テクスチャ色情報を配列にコピーするコストを下げることができます。

しかし、このAPIを使ってアセットとしてインポートしたTexture2Dを直接書き換えてしまうと、アセットそのものも変更されてしまいます。

実例

Texture2Dを受け取って、ネガポジ変換するコードを書きました。

(画像は先日秋葉原に行った時の写真です)

using UnityEngine;

namespace Clay.ImageProcessor
{
    public class NegativeConverter : MonoBehaviour
    {
        [SerializeField] private Texture2D inputTexture;
        
        public void Start()
        {
            var pixels = inputTexture.GetPixelData<Color32>(0);
            for (var i = 0; i < pixels.Length; i++)
            {
                var color = pixels[i];
                color.r = (byte)(255 - color.r);
                color.g = (byte)(255 - color.g);
                color.b = (byte)(255 - color.b);
                pixels[i] = color;
            }
            // 変更を適応する
            inputTexture.Apply();
        }
    }
}

このコードを実行すると……

なんと、元の画像アセットごとネガポジ変換されてしまいました。 また、このスクリプトを再度実行すると、元の画像に戻りました。

改善方法

  1. スクリプト内でTexture2DやRenderTextureとして複製し、それらが持つピクセルデータを書き換える
  2. 一度書き換えてしまった後でも、アセットを右クリックしてReimportすれば元に戻る
    • これで直るのは、Unityが書き換えたのは「元の画像ファイルをエンジン内部で読み取って作ったデータ」であって、大元のJPGファイルは変更されていないからです。

まとめ

UnityのAPIには、不意にアセットそのものを破壊してしまうモノが多数あります。注意して開発を進めましょう。