0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】ReflectionProbe から CubemapArray をつくる

Posted at

概要

複数の ReflectionProbe のベイク済み Cubemap texture を CubemapArray にまとめます.

実装

エディタ拡張にしました.

using UnityEngine;
using System.IO;

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.Rendering;
using System.Collections.Generic;

public class ProbeToCubemapArray : ScriptableWizard
{
    public ReflectionProbe[] probes;

    [MenuItem("Utils/Probe To Cubemap Array")]
    static void Open()
    {
        DisplayWizard<ProbeToCubemapArray>("Probe To Cubemap Array");
    }

    void OnWizardCreate()
    {
        List<Cubemap> cubes = new (probes.Length);
        foreach (var p in probes)
        {
            if (p != null && p.mode == ReflectionProbeMode.Baked && p.bakedTexture != null)
            {
                cubes.Add((Cubemap)p.bakedTexture);
            }
        }
        if (cubes.Count == 0) return;
        CubemapArray cube_array = CubemapToCubemapArray(cubes.ToArray());

        var src_path = AssetDatabase.GetAssetPath(cubes[0]);
        var path = Path.GetDirectoryName(src_path) + "\\" + Path.GetFileNameWithoutExtension(src_path) + "-CubemapArray.asset";
        path = AssetDatabase.GenerateUniqueAssetPath(path);
        AssetDatabase.CreateAsset(cube_array, path);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log($"Saved as \"{path}\"");
    }

    public static CubemapArray CubemapToCubemapArray(Cubemap[] cubes)
    {
        var cube_array = new CubemapArray(cubes[0].width, cubes.Length, TextureFormat.BC6H, cubes[0].mipmapCount, false);
        for (int i = 0; i < cube_array.cubemapCount; i++)
        {
            bool readable = cubes[i].isReadable;
            if (!readable) SetReadable(cubes[i], true);
            // BC6H は mipmap が自動生成されないので,すべてコピーする.
            for (int mip = 0; mip < cube_array.mipmapCount; mip++)
            {
                for (int f = 0; f < 6; f++)
                {
                    cube_array.SetPixelData(cubes[i].GetPixelData<byte>(mip, (CubemapFace)f), mip, (CubemapFace)f, i);
                }
            }
            if (!readable) SetReadable(cubes[i], false);
        }
        cube_array.Apply(false, true);
        cube_array.filterMode = FilterMode.Trilinear;
        cube_array.anisoLevel = 0;
        cube_array.wrapMode = TextureWrapMode.Clamp;
        return cube_array;
    }

    static void SetReadable(Texture texture, bool isReadable)
    {
        if (texture.isReadable == isReadable)
        {
            return;
        }
        var path = AssetDatabase.GetAssetPath(texture);
        var importer = AssetImporter.GetAtPath(path) as TextureImporter;
        if (null == importer)
        {
            return;
        }
        importer.isReadable = isReadable;
        importer.SaveAndReimport();
    }
}
#endif

解説

ReflectionProbe をベイクすると,生成される Cubemap は BC6H というフォーマットになります(HDRの場合).圧縮率が高く優秀なので,CubemapArray も BC6H で作成します.

Texture2D.ReadPixels() は BC6H に対応していないようなので,Get/SetPixel 系の関数で地道にコピーしていきます.その際,Get/SetPixel するテクスチャは readable である必要があります.

Texture.Apply(true) を実行すると mipmap が自動生成されますが,BC6H は mipmap の自動生成ができないため,すべての mipmap をコピーしていきます.

CubemapArray は拡張子を .asset として保存します.ファイルサイズは単純に元の Cubemap のサイズの総和になるようです.

雑記

CubemapArray を調べてみると,情報が非常に少なかったです.それもそのはずで,Culling が効くように適切に Scene を構成していればそもそも CubemapArray は必要ありません.ではなぜ CubemapArray が必要になったのか,という話は,また CubemapArray の使い方の記事でしようと思います.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?