定義済値や定義の意味など、Unityでよしなにやってくれている部分などを含めて単純なDiffuseライティングをやるメモ。
シンプルなコード例
簡単なライティングをするコード例。
SubShader {
Tags {
"RenderType"="Opaque"
"LightMode"="ForwardBase"
}
LOD 200
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertInput {
float4 vertex : SV_POSITION;
float4 normal : NORMAL;
float2 texcoord : TEXCOORD0;
float2 texcoord2 : TEXCOORD1;
};
struct vert2frag {
float4 position : POSITION;
float2 uv : TEXCOORD0;
float2 uv2 : TEXCOORD1;
float4 color : COLOR0;
};
uniform sampler2D _MainTex;
uniform sampler2D _NormalTex;
uniform fixed4 _LightColor0;
vert2frag vert(vertInput v) {
vert2frag o;
float4 wpos = mul(UNITY_MATRIX_MVP, v.vertex);
float4 wnormal = mul(v.normal, _World2Object);
float diffuse = max(0, dot(_WorldSpaceLightPos0, wnormal));
o.position = wpos;
o.color = _LightColor0 * diffuse;
return o;
}
float4 frag(vert2frag i) : COLOR {
return i.color;
}
ENDCG
}
}
ライティングの準備
-
"LightMode"="ForwardBase"
タグをつける
これを付けないとライティングに関する情報が取得できません。
-
uniform fixed4 _LightColor0
を宣言
uniform変数として、fixed4
型の_LightColor0
という変数を宣言します。
ここに、ライトの色が渡されます。
頂点シェーダで計算
頂点シェーダは以下のようになっています。
vert2frag vert(vertInput v) {
vert2frag o;
float4 wpos = mul(UNITY_MATRIX_MVP, v.vertex);
float4 wnormal = mul(v.normal, _World2Object);
float diffuse = max(0, dot(_WorldSpaceLightPos0, wnormal));
o.position = wpos;
o.color = _LightColor0 * diffuse;
return o;
}
wnormalの計算時、normalizeしてしまっていたので削除しました。normalizeするとうまく動きません。
処理としては法線をワールド空間座標に変換したのちに、ライトの位置との内積を取っています。
_WorldSpaceLightPos0
はひとつ目のライトの位置です。これは自動的に宣言されているのでそのまま利用することが出来ます。
ちなみにUnityでは、DirectionalLightも視覚的に位置が表示されますが、実際には回転が影響を与えます。(当たり前ですが)
視覚的に見えている分、おや?となってしまうので勘違いしないようにしましょうw
法線の計算
法線の計算については、ワールド空間座標への変換を行っていますが、変換には若干の注意が必要です。
(こちらの記事がとても参考になりました)
結論だけ言うと、 モデル行列の逆転置行列を掛ける 必要があります。
が、そこは少しだけ楽をして、Unityから渡されるモデル行列の逆行列(_World2Object
)を、ベクトルに対して逆から掛けてやることで結果的に逆転置行列を掛けたことにしています。
色の計算
あとは、ライトの色と法線とライト位置から求めた内積を掛けあわせて最終的な色を決定している、というわけです。