【Unity】ランタイムで生成したテクスチャを圧縮して保存・ロードする

Unityでランタイムで生成したテクスチャを圧縮して保存・ロードする方法です。

Unity6.0.27f1

やりたいこと

Unityでランタイムで生成したテクスチャは、そのままではメモリ使用量が大きくなってしまいます。
例えば、1024×1024のRGB24テクスチャは約3MBのメモリを消費します。

そこでこの記事では、ランタイムで生成したテクスチャを圧縮してメモリ使用量を削減する方法についてまとめます。

方法とソースコード

ランタイムでテクスチャを圧縮するには Texture2D.Compress を使用します。
これは各プラットフォームごとに決まったフォーマットでテクスチャを圧縮してくれます。

docs.unity3d.com

保存についてはバイナリをそのまま保存し、ロードは Texture2D.LoadRawTextureData を使って行います。
以下はこの一連の流れを行うサンプルです。

using System.IO;
using UnityEngine;

public class TextureCompressionExample : MonoBehaviour
{
    private const int TextureSize = 1024;

    private void Start()
    {
        var compressedFilePath = Path.Combine(Application.persistentDataPath, "compressed.bytes");

        // 1. テクスチャを生成
        var originalTexture = GenerateTexture();
        Debug.Log($"生成直後: {GetMemorySizeInMb(originalTexture):F2} MB");

        // 2. テクスチャを圧縮
        var compressedTexture = CompressTexture(originalTexture);
        Debug.Log($"圧縮後: {GetMemorySizeInMb(compressedTexture):F2} MB");
        Debug.Log($"圧縮フォーマット: {compressedTexture.format}");

        // 3. ファイルに保存
        SaveTexture(compressedTexture, compressedFilePath);

        // 4. ファイルからロード
        LoadAndVerify(compressedFilePath);

        // メモリ解放
        Destroy(originalTexture);
        Destroy(compressedTexture);
    }

    private Texture2D GenerateTexture()
    {
        // RGB24形式(アルファなし)でテクスチャを作成
        var texture = new Texture2D(TextureSize, TextureSize, TextureFormat.RGB24, false);

        // 適当に着色
        var pixels = new Color[TextureSize * TextureSize];
        var noiseStrength = 0.1f;

        for (var y = 0; y < TextureSize; y++)
        for (var x = 0; x < TextureSize; x++)
        {
            var r = (float)x / TextureSize;
            var g = (float)y / TextureSize;
            var b = Mathf.Sin(x * 0.01f) * 0.5f + 0.5f;

            r += Random.Range(-noiseStrength, noiseStrength);
            g += Random.Range(-noiseStrength, noiseStrength);
            b += Random.Range(-noiseStrength, noiseStrength);

            r = Mathf.Clamp01(r);
            g = Mathf.Clamp01(g);
            b = Mathf.Clamp01(b);

            pixels[y * TextureSize + x] = new Color(r, g, b);
        }

        texture.SetPixels(pixels);
        texture.Apply();

        return texture;
    }

    private Texture2D CompressTexture(Texture2D original)
    {
        var compressed = new Texture2D(original.width, original.height, original.format, false);
        compressed.SetPixels(original.GetPixels());
        compressed.Apply();
        compressed.Compress(true);

        return compressed;
    }

    private void SaveTexture(Texture2D compressed, string filePath)
    {
        // 圧縮テクスチャの生データを保存
        var rawData = compressed.GetRawTextureData();
        File.WriteAllBytes(filePath, rawData);
        Debug.Log($"圧縮データ保存: {rawData.Length / (1024f * 1024f):F2} MB");

        // フォーマット情報も保存(実際の使用時は別ファイルやヘッダーに含める)
        PlayerPrefs.SetInt("CompressedTextureFormat", (int)compressed.format);
        PlayerPrefs.SetInt("CompressedTextureWidth", compressed.width);
        PlayerPrefs.SetInt("CompressedTextureHeight", compressed.height);
        PlayerPrefs.Save();
    }

    private void LoadAndVerify(string filePath)
    {
        // 保存したフォーマット情報を取得
        var format = (TextureFormat)PlayerPrefs.GetInt("CompressedTextureFormat");
        var width = PlayerPrefs.GetInt("CompressedTextureWidth");
        var height = PlayerPrefs.GetInt("CompressedTextureHeight");

        // 圧縮データを読み込み
        var compressedData = File.ReadAllBytes(filePath);

        // 圧縮フォーマットでテクスチャを作成
        var loadedTexture = new Texture2D(width, height, format, false);
        loadedTexture.LoadRawTextureData(compressedData);
        loadedTexture.Apply();

        Debug.Log($"ロード後: {GetMemorySizeInMb(loadedTexture):F2} MB");
        Debug.Log($"ロード後フォーマット: {loadedTexture.format}");

        // メモリ解放
        Destroy(loadedTexture);
    }

    private static float GetMemorySizeInMb(Texture2D texture)
    {
        return texture.GetRawTextureData().Length / (1024f * 1024f);
    }
}

実行結果

これを手元のPC(プラットフォームはiOS)で実行した結果、以下のログ出力が得られました。

生成直後: 3.00 MB
圧縮後: 0.50 MB
圧縮フォーマット: ETC2_RGB
圧縮データ保存: 0.50 MB
ロード後: 0.50 MB
ロード後フォーマット: ETC2_RGB

参考

docs.unity3d.com