Version: 2020.2
言語: 日本語
キューブマップ
シェーダーを書く

キューブマップ配列

キューブマップ配列とは、同じサイズ、同じフォーマットのキューブマップの配列で、GPU は 1 つのテクスチャリソースとしてアクセスできます。キューブマップ配列は、効率的なリフレクションプローブ、ライト、影のシステムの実装によく使われます。

Unity プロジェクトでは、Unity エディターはキューブマップ配列をテクスチャアセットとして表します。テクスチャアセットのインポート設定を行うには Inspector を使用したり、テクスチャアセットを選択して TextureImporter API を使用するスクリプトを作成します。Unity エンジンでは、Unity は CubemapArray クラスを使ってキューブマップ配列を表します。

キューブマップ配列の作成

プロジェクトでキューブマップ配列を作成するには、スクリプトを使用する必要があります。

以下の例は、CubemapArray クラスのインスタンスを作成し、それにカラーデータを入力し、それをテクスチャアセットとしてプロジェクトに保存するエディタースクリプトです。

using UnityEngine;
public class CreateCubeArrayTexture : MonoBehaviour
{
    [UnityEditor.MenuItem("CreateExamples/CubemapArray")]
    static void CreateCubemapArray()
    {
        //キューブマップ配列とカラーデータを設定
        int faceSize = 16;
        int arraySize = 4;
        int[] kCubeXRemap = new int[] { 2, 2, 0, 0, 0, 0 };
        int[] kCubeYRemap = new int[] { 1, 1, 2, 2, 1, 1 };
        int[] kCubeZRemap = new int[] { 0, 0, 1, 1, 2, 2 };
        float[] kCubeXSign = new float[] { -1.0F, 1.0F, 1.0F, 1.0F, 1.0F, -1.0F };
        float[] kCubeYSign = new float[] { -1.0F, -1.0F, 1.0F, -1.0F, -1.0F, -1.0F };
        float[] kCubeZSign = new float[] { 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F };
        var baseCols = new Color[] { Color.white, new Color(1, .5f, .5f, 1), new Color(.5f, 1, .5f, 1), new Color(.5f, .5f, 1, 1), Color.gray };
        
        // CubemapArray のインスタンスを作成
        var tex = new CubemapArray(faceSize, arraySize, TextureFormat.ARGB32, true);
        tex.filterMode = FilterMode.Trilinear;
        
        // 各キューブマップを反復
        var col = new Color[tex.width * tex.width];
        float invSize = 1.0f / tex.width;
        for (var i = 0; i < tex.cubemapCount; ++i)
        {
            var baseCol = baseCols[i % baseCols.Length];

            // 現在のキューブマップの各面を反復
            for (var face = 0; face < 6; ++face)
            {
                var idx = 0;
                Vector3 signScale = new Vector3(kCubeXSign[face], kCubeYSign[face], kCubeZSign[face]);
                
                // 現在の面の各ピクセルを反復
                for (int y = 0; y < tex.width; ++y)
                {
                    for (int x = 0; x < tex.width; ++x)
                    {
                        // 現在のピクセルの "法線方向" の色を計算
                        Vector3 uvDir = new Vector3(x * invSize * 2.0f - 1.0f, y * invSize * 2.0f - 1.0f, 1.0f);
                        uvDir = uvDir.normalized;
                        uvDir.Scale(signScale);
                        Vector3 dir = Vector3.zero;
                        dir[kCubeXRemap[face]] = uvDir[0];
                        dir[kCubeYRemap[face]] = uvDir[1];
                        dir[kCubeZRemap[face]] = uvDir[2];

                        // 色を 0.4..1.0 の範囲にシフト
                        Color c = new Color(dir.x * 0.3f + 0.7f, dir.y * 0.3f + 0.7f, dir.z * 0.3f + 0.7f, 1.0f);
                        
                        // ミップマップがよりはっきりと見えるように、いくつかのピクセルにパターンを追加
                        if (((x ^ y) & 3) == 1)
                            c *= 0.5f;
                        
                        // baseCol の色合いで色を着色
                        col[idx] = baseCol * c;
                        ++idx;
                    }
                }

                // この面のカラー値をテクスチャにコピー
                tex.SetPixels(col, (CubemapFace)face, i);
            }
        }

        // テクスチャに変更を適用し、更新されたテクスチャを GPU にアップロード
        tex.Apply();        

        //テクスチャを Unity プロジェクトに保存
        AssetDatabase.CreateAsset(tex, "Assets/ExampleCubemapArray.asset");
        UnityEditor.AssetDatabase.SaveAssets();
    }
}

キューブマップ配列のプレビュー

Inspector ウィンドウでキューブマップ配列をプレビューするには、Project ウィンドウに移動して、 Texture Asset を選択します。このテクスチャアセットのテクスチャインポート設定が Inspector に表示され、Inspector の下部にキューブマップ配列のプレビューが描画されます。

ツールバーには以下が用意されています。

コントロール 機能
Filter Mode プレビューに使用するフィルターです。Filter Mode のドキュメントを参照してください。

シェーダーでのキューブマップ配列の使用

以下は、キューブマップ配列を使用したシェーダーサンプルです。

Shader "CubemapArrayShaderExample" {
Properties {
    _MainTex ("CubemapArray", CubeArray) = "" {}
    _Mip ("Mip", Float) = 0.0
    _Intensity ("Intensity", Float) = 1.0
    _SliceIndex ("Slice", Int) = 0
    _Exposure ("Exposure", Float) = 0.0
}

SubShader {
    Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "ForceSupported" = "True"}

    Pass {

        CGPROGRAM
        #pragma vertex vert
            #pragma fragment frag
            #pragma require sampleLOD
            #pragma require cubearray
            #include "UnityCG.cginc"
    
    
    
            struct appdata {
                float4 pos : POSITION;
                float3 nor : NORMAL;
            };
    
            struct v2f {
                float3 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
    
            uniform int _SliceIndex;
            float _Mip;
            half _Alpha;
            half _Intensity;
            float _Exposure;
    
           v2f vert (appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.pos);
                float3 viewDir = -normalize(ObjSpaceViewDir(v.pos));
                o.uv = reflect(viewDir, v.nor);
                return o;
            }
    
            half4 _MainTex_HDR;
            UNITY_DECLARE_TEXCUBEARRAY(_MainTex);
            fixed4 frag (v2f i) : COLOR0
            {
                fixed4 c = UNITY_SAMPLE_TEXCUBEARRAY(_MainTex, float4(i.uv, _SliceIndex));
                fixed4 cmip = UNITY_SAMPLE_TEXCUBEARRAY_LOD(_MainTex, float4(i.uv, _SliceIndex), _Mip);
                if (_Mip >= 0.0)
                    c = cmip;
                c.rgb = DecodeHDR (c, _MainTex_HDR) * _Intensity;
                c.rgb *= exp2(_Exposure);
                c = lerp (c, c.aaaa, _Alpha);
                return c;
            }
            ENDCG
        }
    }
    Fallback Off
}

ページ上の例で作成したキューブマップ配列でこのシェーダーを使用すると、 結果は以下のようになります。


キューブマップ配列のサポートは、2020.1 で追加NewIn20201

キューブマップ
シェーダーを書く