Unityのシェーダーでキューブマップを描画するには

鏡面反射に必須なキューブマップの映り込みを実装します。スカイボックスの映り込みやリフレクションプローブの映り込みも、基本的な原理は同じなので、非常に応用が利く実装です。

Contents

キューブマップ用のフィールドをインスペクターに表示する

今回のシェーダーはキューブマップを使用しますので、マテリアルのインスペクターからキューブマップテクスチャを設定できるようにしておきます。キューブマップを設定できるようにするには、プロパティタイプをCUBEにする必要があるので、注意してください。

Properties
{
    _Cube("Cube", CUBE) = "" {} //キューブマップを使用するにはCUBEに設定する
}

テクスチャをキューブマップに設定する

上記でキューブマップ用のフィールドを表示しましたが、そこに張り付けるテクスチャには特殊な設定が必要です。通常、Unityにインポートされたテクスチャは、Texture Shapeが2Dになっていますが、キューブマップとして使用するには、Texture ShapeをCubeに設定する必要があります。

なお、キューブマップを持っていないというかたは、以下の記事にフリーテクスチャサイトをまとめていますので、ぜひそちらからダウンロードしてみてください。

キューブマップからサンプリングする

キューブマップからサンプリングするには、以下のように反射ベクトルを計算し、UNITY_SAMPLE_TEXCUBEから色をサンプリングします。なお、プロパティで設定したテクスチャをキューブマップとして使用するためには、UNITY_DECLARE_TEXCUBE(テクスチャ変数名)を宣言する必要があります。

UNITY_DECLARE_TEXCUBE(_Cube); //キューブマップとして使用することを宣言

fixed4 frag (v2f i) : SV_Target
{
    float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos); //視線ベクトルを計算
    float3 refDir = reflect(-viewDir, i.normal); //反射ベクトルを計算

    fixed4 col = UNITY_SAMPLE_TEXCUBE(_Cube, refDir); //キューブマップからサンプリング
    return col;
}

反射ベクトルはreflect関数によって簡単に求めることができます。計算に用いる視線のベクトル方向には注意してください。

シェーダー全体

以上の実装をすべてまとめたシェーダーの全容は、以下の通りです。

Shader "CAGraphicsAcademy/Cubemap"
{
    Properties
    {
        _Cube("Cube", CUBE) = "" {} //キューブマップを使用するにはCUBEに設定する
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : Normal;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                float3 normal : TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            UNITY_DECLARE_TEXCUBE(_Cube); //キューブマップとして使用することを宣言

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex); //頂点をMVP行列変換
                o.uv = TRANSFORM_TEX(v.uv, _MainTex); //テクスチャスケールとオフセットを加味
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; //頂点座標をワールド座標系に変換
                o.normal = UnityObjectToWorldNormal(v.normal); //法線をワールド座標系に変換
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos); //視線ベクトルを計算
                float3 refDir = reflect(-viewDir, i.normal); //反射ベクトルを計算

                fixed4 col = UNITY_SAMPLE_TEXCUBE(_Cube, refDir); //キューブマップからサンプリング
                return col;
            }
            ENDCG
        }
    }
}

返信を残す

メールアドレスが公開されることはありません。