Unity シェーダーチュートリアル ノーマルマップ
シェーダー
法線方向を変えることで擬似的に凹凸を表現するノーマルマップは、
ローポリゴンにおける質感設定では外せません。
今回は、効率的なノーマルマップの設定方法とUVの使い方について説明します。
まず、以下のコードを見てください。
初回シェーダーチュートリアルの基本的なシェーダーコードに、
ノーマルマップの設定を追加したものです。
ノーマルマップの設定項目
プロパティ
Propaties にノーマルマップのテクスチャを指定する _BumpMap と
強度を指定する _BumpScale が追加されています。
_BumpMap ("Normal Map" , 2D ) = "bump"{}
_BumpScale ("Normal Scale", Range(0, 1)) = 1.0
強度を設定するプロパティは無くても問題ありませんが
その場合、丁度良い凹凸感にたどり着くまでに、
Unity と画像編集ソフトを行ったり来たりすることになりそうです。
ハイポリからローポリに転写したノーマルマップの場合は必要なさそうですが、
単純に細かい凹凸を表現するためのノーマルマップの場合は、
強度設定があった方が、スライダーをグリグリしながら、より直観的に設定できます。
ちなみに、プロパティ名の _BumpMap と _BumpScale について、
なんで、_NormalMap とか _NormalTex とか _NormalScale ではないのかと思った人はいませんか?
理由は Unity のビルトインシェーダーがそういうプロパティ名になっているからです。
同じ名前にしておくことで、シェーダーを入れ替えても値がそのまま引き継がれます。
「使うためにシェーダー作ってるんだから、入れ替えなんてしませんよ。」
とも思いますが、何と無く、念のためです。
斯く言う私は、_NormalTex とか _NormalScale とかにしたい派です。
サーフェースシェーダー
まず、Propaties で設定した項目を使用できるように、SubShader 内でも宣言します。
sampler2D _BumpMap;
half _BumpScale;
そして、サーフェースシェーダーの関数内で、テクスチャをサンプリングし、
ノーマル用の接続である o.Normal につなぎます。
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 n = tex2D(_BumpMap, IN.uv_MainTex);
o.Normal= UnpackScaleNormal(n, _BumpScale);
}
UnpackScaleNormal はビルトインで定義されている関数で、
o.Normal に接続する前に必ず通す必要があります。
強度の設定がない場合は UnpackNormal を使用します。
UnpackScaleNormal(texture, scale);
UnpackNormal(texture);
注意点として、引数として指定するテクスチャは fixed4 なのに対し、
UnpackScaleNormal や UnpackNormal の返り値は fixed3 なので、
分けて記述する場合は以下のようになります。
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 n = tex2D(_BumpMap, IN.uv_MainTex);
fixed3 unpack = UnpackScaleNormal(n, _BumpScale);
o.Normal= unpack;
}
UVについて
上記シェーダーコードで、テクスチャをサンプリングしている部分を抜き出すとこうなります。
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
fixed4 n = tex2D(_BumpMap, IN.uv_MainTex);
}
なんでこうじゃないのかと思った方はいませんか?
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
fixed4 n = tex2D(_BumpMap, IN.uv_BumpMap);
}
1つのUVを定義して両方のテクスチャに使用する事も出来ますし、
テクスチャごとにUVを定義して使用する事もできます。
前者はインスペクター上の_MainTex で設定したUVのタイリング設定が、
_BumpMap にも使い回しされるのに対し、
後者は、_MainTex と_BumpMap それぞれでタイリングを設定できます。
入力構造体(struct Input)で定義したUV、例えば uv_MainTex は
「_MainTex 用にUVを使えるようにする」
と言う意味ではありません。
「_MainTex のタイリング設定を施したUVをどこでも使えるようにする」
という感じです。
ほとんどの場合、アルベドテクスチャとノーマルテクスチャは同じUV空間で作られるので、
わざわざ2つのUVを定義するより、1つだけ定義して使い回した方がスマートです。
また、アーティストから
「_BumpMap のタイリングが効かないんだけど 💢」
と言われてしまわないように、タイリング表示を消すこともできます。
[NoScaleOffset] _BumpMap ("Normal Map", 2D) = "bump"{}
プロパティの前に [NoScaleOffset] と記述すれば、タイリング表示が消えます。
ノーマルマップのブレンド
複数のノーマルマップをブレンドしたい、例えば以下のように、
1つ目のノーマルマップで、タイルの凹凸感を、
2つ目のノーマルマップで、表面がボコボコしている感じ、を表現したい場合、
2つのノーマルマップをブレンドする必要があります。
以下が、主要部分の抜粋コードです。
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap2;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed3 n = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale);
fixed3 n2 = UnpackScaleNormal(tex2D(_BumpMap2, IN.uv_BumpMap2), _BumpScale2);
o.Normal= BlendNormals(n, n2);
}
タイルのノーマルマップは、アルベドと同じタイリングで良いので、uv_MainTex を使い回し、
ボコボコのノーマルマップは、大きさを調整できるように、
あえて自身のタイリング設定でUVを定義しています。(uv_BumpMap2)
そして、2つのノーマルマップを、それぞれ強度付きでサンプリングした後、(UnpackScaleNormal)
BlendNormals() を通して、o.Normal に接続しています。
BlendNormals() はビルトインで定義されている関数で、
2つの Unpack 済みノーマルマップをブレンドします。
BlendNormals(unpackedNormal1, unpackedNormal2);
[…] シェーダーチュートリアル ノーマルマップ | Tsumiki Tech Times […]