今回はUnityのShader勉強枠です。
UnityのレファレンスにあるサーフェースShaderの例からShaderを勉強しています。
前回はTextureを持つshaderを書きました。
〇法線マップとは?
法線マップ(NormalMap)はバンプマップ(BumpMap)の一種です。
一言で説明するならデコボコ感を再現する技法です。
バンプマップとはCG表現のひとつでレンダリング(描画)するオブジェクト表面の法線に揺らぎを与えて凸凹に見えるようにする技法です。
バンプマップでは単純に「高さ」のみが再現できますが、ノーマルマップではx,y,zの各方向を持つことができます。
〇ノーマルマップを使用することでどのようなことができるか?
ノーマルマップを使用すると凹凸を再現できます。
この画像ではメインテクスチャを使用しておらず、UnityのPrimitive3DCubeにノーマルマップのマテリアルを適用していますが、ぼこぼこしていることがわかります。
モデル自体はCubeで、凸凹しているわけではなくノーマルマップに適応したテクスチャーで再現しています。
もしこれをモデリングで再現しようとした場合、ポリゴン数が一気に上がることが予想できます。
効果的にノーマルマップを使用することで様々な表現ができます。
〇Shader
前回のDiffuse Texture Shaderを基にノーマルマップを扱うShaderを書きます。
●Diffuse Texture Shader
Shader "Example/Diffuse Texture" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; }; sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG } Fallback "Diffuse" }
まず、ノーマルマップのTextureをMaterialで扱うためにPropertiesに記述します。
Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} }
_MainTex同様2Dテクスチャとして扱います。
次に使用するテクスチャ情報を記述するInput構造体で_BumpMapをuvとして使用することを記述します。
struct Input { float2 uv_MainTex; float2 uv_BumpMap; };
プロパティで受け取ったデータをシェーダ内で扱うための定義であるsampler2Dを宣言します。
sampler2D _BumpMap;
最後に実際の処理を記述します。
void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); }
ここでは[UnpackNormal]という定義の中で処理が行われています。
この定義はUnityCG.incというUnityで用意されているShaderのビルドインシェーダーヘルパー機能でされています。
ビルドインシェーダーヘルパー機能というのはUnityでshaderを簡単に書けるように用意された定義済みの関数です。
定義を見ていくと以下のようになっています。
inline fixed3 UnpackNormal(fixed4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalDXT5nm(packednormal); #endif }
一気に難しくなりましたがどうやら[Unity_NO_DXT5nm]が定義されている場合で分岐されているようです
UNITY_NO_DXT5nmは DXT5NM をサポートしないプラットフォームのシェーダをコンパイルするときに設定されるようです。
DXT5NMとはDirectXのDXTC(Direct X Texture Compression)という規格でテクスチャの圧縮にかかわっているようです。
この辺りは以下のブログがとても参考になります。(自分も頑張ってこのくらい詳しくなりたい。)
定義がない場合UnpackNormalDXT5nmという定義で処理が行われているようです。
内部まで踏み込むと沼なので、一度戻り
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
でごにょごにょした結果を返すことでノーマルマップを行っています。
Diffuse Textureをノーマルマップに対応したものをDiffuse Bumpと名前を変更します。 以下のようになります。
Shader "Example/Diffuse Bump" { Properties { _MainTex ("Texture", 2D) = "white" {} _BumpMap ("Bumpmap", 2D) = "bump" {} } SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_BumpMap; }; sampler2D _MainTex; sampler2D _BumpMap; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); } ENDCG } Fallback "Diffuse" }