ADSシェーダをテクスチャ対応させる

はじめに

以前にADSシェーダを作成しました。 今回はこのシェーダの拡散反射の色を単色ではなくテクスチャから指定するように改造します。

実行結果

Unityのバージョン:2018.3.0b4

ソースコードの解説

全文

まずはソースコードを全文はります。

Shader "MyShader/PixelADSTexture"
{
  Properties
  {
    [Header(Light)]
    _LightDirection ("Light Direction", Vector) = (1.0, 1.0, -1.0, 1.0)
    _Lambient ("Lambient", Vector) = (0.5, 0.5, 0.5, 1.0)
    _Ldiffuse ("Ldiffuse", Vector) = (1.0, 1.0, 1.0, 1.0)
    _Lspecular ("Lspecular", Vector) = (1.0, 1.0, 1.0, 1.0)
    [Space]
    [Header(Material)]
    _MainTex ("Diffuse Texture", 2D) = "white" {}
    _Kambient ("Kambient", Vector) = (0.1, 0.1, 0.1, 1.0)
    _Kspecular ("Kspecular", Vector) = (0.5, 0.5, 0.5, 1.0)
    _Shininess ("Shininess", float) = 150
  }
  SubShader
  {
    Pass
    {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"

      struct appdata
      {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float2 uv : TEXCOORD0;
      };

      struct v2f
      {
        float4 vertex : SV_POSITION;
        float3 normal : NORMAL;
        float4 position : TEXCOORD0;
        float2 uv : TEXCOORD1;
      };

      uniform float3 _LightDirection;
      uniform float3 _Lambient;
      uniform float3 _Ldiffuse;
      uniform float3 _Lspecular;

      uniform sampler2D _MainTex;
      uniform float4 _MainTex_ST;

      uniform float3 _Kambient;
      uniform float3 _Kspecular;
      uniform float _Shininess;

      void vert (in appdata v, out v2f o)
      {
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.normal = v.normal;
        o.position = mul(UNITY_MATRIX_M, v.vertex);

        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
      }

      void frag (in v2f i, out float4 col : SV_Target)
      {
        float3 normal = UnityObjectToWorldNormal(i.normal);
        float3 l = normalize(_LightDirection);
        float3 view = normalize(_WorldSpaceCameraPos - i.position);
        float3 h = normalize(l + view);

        float3 Iambient = _Lambient * _Kambient;

        float3 tex = tex2D (_MainTex, i.uv);
        float3 Idiffuse = _Ldiffuse * tex * max(dot(normal, _LightDirection), 0);

        float3 Ispecular = _Lspecular * _Kspecular * pow(max(dot(normal, h), 0), _Shininess);

        col = float4(Iambient + Idiffuse + Ispecular, 1);
      }
      ENDCG
    }
  }
}

プロパティ

プロパティと対応する変数の宣言の部分は次のとおりです。

Shader "MyShader/PixelADSTexture"
{
  Properties
  {
    [Header(Light)]
    _LightDirection ("Light Direction", Vector) = (1.0, 1.0, -1.0, 1.0)
    _Lambient ("Lambient", Vector) = (0.5, 0.5, 0.5, 1.0)
    _Ldiffuse ("Ldiffuse", Vector) = (1.0, 1.0, 1.0, 1.0)
    _Lspecular ("Lspecular", Vector) = (1.0, 1.0, 1.0, 1.0)
    [Space]
    [Header(Material)]
    _MainTex ("Diffuse Texture", 2D) = "white" {}
    _Kambient ("Kambient", Vector) = (0.1, 0.1, 0.1, 1.0)
    _Kspecular ("Kspecular", Vector) = (0.5, 0.5, 0.5, 1.0)
    _Shininess ("Shininess", float) = 150
  }
  SubShader
  {
    Pass
    {
      CGPROGRAM

      ...

      uniform float3 _LightDirection;
      uniform float3 _Lambient;
      uniform float3 _Ldiffuse;
      uniform float3 _Lspecular;

      uniform sampler2D _MainTex;
      uniform float4 _MainTex_ST;

      uniform float3 _Kambient;
      uniform float3 _Kspecular;
      uniform float _Shininess;

      ...

      ENDCG
    }
  }
}

前回と変わったのは_Kdiffuseのかわりに_MainTexを用意しているところですね。

_MainTex ("Texture", 2D) = "white" {}がテクスチャのプロパティです。 "white"でデフォルトのテクスチャを白にしています。 {}の部分は昔のUnityでパラメータを渡すのに使われていたもののようです。 現在は使われていないようです。

テクスチャはsampler2D型で受け取ります。

_MainTexの他に_MainTex_STという変数が用意されています。 _MainTex_STという変数は_MainTexを用意するとUnityによって裏で作られる変数です。 テクスチャのプロパティを用意すると、 テクスチャの変数の後ろに_STをくっつけた変数が宣言されます。 この変数にはテクスチャのタイリングとオフセットの値が入っています。 _STのxy成分がuvのスケーリングで、zw成分がuvの平行移動成分になっています。

テクスチャのタイリングとオフセットの設定

構造体

頂点シェーダの入力の構造体は次のとおりです。

struct appdata
{
  float4 vertex : POSITION;
  float3 normal : NORMAL;
  float2 uv : TEXCOORD0;
};

uv座標をTEXCOORD0で受け取っています。

頂点シェーダからフラグメントシェーダへ渡す構造体は次のとおりです。

struct v2f
{
  float4 vertex : SV_POSITION;
  float3 normal : NORMAL;
  float4 position : TEXCOORD0;
  float2 uv : TEXCOORD1;
};

uv座標を受け渡すようにしています。

頂点シェーダ

頂点シェーダのコードは次のとおりです。

void vert (in appdata v, out v2f o)
{
  o.vertex = UnityObjectToClipPos(v.vertex);
  o.normal = v.normal;
  o.position = mul(UNITY_MATRIX_M, v.vertex);

  o.uv = TRANSFORM_TEX(v.uv, _MainTex);
}

以前と比べてo.uv = TRANSFORM_TEX(v.uv, _MainTex);の処理が追加されています。 TRANSFORM_TEXはUnityが用意しているマクロの1つです。 これは次のような処理に展開されます。

o.uv = v.uv * _MainTex_ST.xy + _MainTex_ST.zw;

フラグメントシェーダ

void frag (in v2f i, out float4 col : SV_Target)
{
  float3 normal = UnityObjectToWorldNormal(i.normal);
  float3 l = normalize(_LightDirection);
  float3 view = normalize(_WorldSpaceCameraPos - i.position);
  float3 h = normalize(l + view);

  float3 Iambient = _Lambient * _Kambient;

  float3 tex = tex2D (_MainTex, i.uv);
  float3 Idiffuse = _Ldiffuse * tex * max(dot(normal, _LightDirection), 0);

  float3 Ispecular = _Lspecular * _Kspecular * pow(max(dot(normal, h), 0), _Shininess);

  col = float4(Iambient + Idiffuse + Ispecular, 1);
}

float3 tex = tex2D (_MainTex, i.uv);がテクスチャから色を読み出すところです。 読み出した色をdiffuseの色として使っています。

実行結果

テクスチャとして次の画像を与えます。

テクスチャ

テクスチャ

描画結果は次のようになります。

実行結果

タイリングとオフセットを変化させてみます。

タイリングとオフセットを変更

確かに変更されていることがわかります。

タイリングとオフセットを変更

タイリングとオフセットを変更

おわりに

今回は自作のシェーダにテクスチャを与えてみました。

ソースコードはこちらのリポジトリにおいてあります。

  • Unity
  • Shader