夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

UnityでShaderを勉強する その⑤ 法線マップを扱うShaderを書く

 今回はUnityのShader勉強枠です。

 UnityのレファレンスにあるサーフェースShaderの例からShaderを勉強しています。

docs.unity3d.com

 前回はTextureを持つshaderを書きました。

redhologerbera.hatenablog.com

〇法線マップとは?

法線マップ(NormalMap)はバンプマップ(BumpMap)の一種です。

一言で説明するならデコボコ感を再現する技法です。

バンプマップとはCG表現のひとつでレンダリング(描画)するオブジェクト表面の法線に揺らぎを与えて凸凹に見えるようにする技法です。

バンプマップでは単純に「高さ」のみが再現できますが、ノーマルマップではx,y,zの各方向を持つことができます。

docs.unity3d.com

〇ノーマルマップを使用することでどのようなことができるか?

 ノーマルマップを使用すると凹凸を再現できます。

 f:id:Holomoto-Sumire:20200412122613j:plain

 この画像ではメインテクスチャを使用しておらず、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を簡単に書けるように用意された定義済みの関数です。

docs.unity3d.com

 定義を見ていくと以下のようになっています。

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)という規格でテクスチャの圧縮にかかわっているようです。

 この辺りは以下のブログがとても参考になります。(自分も頑張ってこのくらい詳しくなりたい。)

t-tutiya.hatenablog.com

定義がない場合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"
    }