Unityで自作シェーダーで法線マップ(Normal Map)を使うときに注意すること

こんにちは。ヤマヤタケシです。

前回、法線マップの理論は見えた!ということで実装を始めました。
やっぱり、理論と実践は違いますね。
というか、理論だけでは実装ができないという現実・・・。

普通に取得しても、x,y,zの色が同じになったのです。

float3 normal = tex2D (_NormalTex, i.uv);
float4 col = float4(col.xyz, 1);
return col;

Unityのテクスチャの設定をNormal map -> Color に戻すとちゃんと、x,y,zが別のものが入ってきました。

???

調べました。
Unity、DirectX環境でのNormalMapの内部的なフォーマットお勉強
「GLESやスマホフラグが立っていない環境ではDirectXモードということでNormalDiffuseの法線サンプリング計算はDXT5nを前提に「YW要素をXYZWのXYに変換し、Z要素をXYから復元して埋める」計算に切り替わってますね。」

な、なんだってー!
どうやら、テクスチャにはXXXYな形で入っているので、tex2Dのxyzは同じ値になるんですね。
知らんがな・・・。

ということで、UnityCG.incにUnpackNormal が定義されています。

inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
	return packednormal.xyz * 2 - 1;
#else
	return UnpackNormalDXT5nm(packednormal);
#endif
}

結論!
こんな感じで解決です。

float3 normal = UnpackNormal (tex2D (_NormalTex, i.uv));
float4 col = float4(col.xyz, 1);
return col;

それにしても、float3, half3, fixed3 のどれを使えば良いのやら・・・。

ありました。
シェーダーを書く場合のパフォーマンスのヒント
引用します。

ワールド空間の位置とテクスチャ座標には、float 型を使用してください。
上記以外 (ベクトル、HDR カラーなど) には、half で始めます。必要なときだけ、増加します。
テクスチャデータのとても簡易な操作には、fixed を使用します。

なるほど!
と、思った次の行には・・・。
あらゆる現代のデスクトップ GPU は、常にすべての事を完全な 浮動小数点数精度で計算していて、そのため、根底では float/half/fixed の結果はまったく同じになります。

なんてこった。
じゃあ、floatを使うさ!
ただ、モバイルでは影響があるらしいです。

そんじゃまた。