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