シェーダー: ShaderLab と固定関数シェーダー
Physics

シェーダー: 頂点とフラグメントプログラム

このチュートリアルでは自身で Unity シェーダーで頂点とフラグメントプログラムを書く方法を学びます。ShaderLab の基本の紹介については シェーダー: ShaderLab と固定関数シェーダー を参照してください。もしライティングに作用するシェーダーを書く場合は代わりに サーフェスシェーダーの記述 について読んでください。

最初にシェーダーの一般的な構造の復習から始めます。

Shader "MyShaderName"
{
    Properties
    {
        // material properties here
    }
    SubShader // subshader for graphics hardware A
    {
        Pass
        {
            // pass commands ...
        }
        // more passes if needed
    }
    // more subshaders if needed
    FallBack "VertexLit" // optional fallback
}

ここで、最後の行の新しいコマンド、FallBack “VertexLit” を説明します。 Fallback コマンドは、シェーダーの最後に使用されます。グラフィックスハードウェアで、使用しているシェーダーの サブシェーダー が 1つも実行できない場合に、他のシェーダーを指定してそれに含まれるサブシェーダーを使用します。まるで、別のシェーダーのすべてのサブシェーダーが最後の行の Fallback シェーダーに含まれているような効果があります。

例えば、もし、洗練されたノーマルマッピングのシェーダーを作成しているときに、 わざわざ、古いグラフィックスカード用に非常に基本的な非ノーマルマッピングのサブシェーダーを書く代わりに、 フォールバックして、 ビルトインの VertexLit シェーダーを使用します。

シェーダーの基本は、 シェーダー: ShaderLab と固定関数シェーダー で紹介しています。 また、プロパティーサブシェーダーパス の完全なドキュメントも利用してください。

サブシェーダーを素早く作成する方法は、他のシェーダーで定義されたパスを使用することです。 UsePass コマンドは、それを実行するのにちょうどぴったりです。 このコマンドを使用すると、シェーダーコードを効率よく再使用することができます。 以下のコマンド例は、 ビルトインの Specular シェーダーの “FORWARD” という名のパスを使用することを表しています。 UsePass “Specular/FORWARD”.

UsePass を利用するには、 使用したいパスに名前をつける必要があります。パスの中の Name コマンドにより 名前がつけられます。Name “MyPassName”

頂点およびフラグメントプログラム

最初のチュートリアル でひとつの texture combine コマンドだけを使用するパスを説明しました。次に自身のパスで頂点およびフラグメントプログラムを使用する方法をみていきます。

頂点およびフラグメントプログラムを使用するとき(いわゆる “プログラマブルパイプライン”)、グラフィックスカードにあるほとんどのハードコードされた機能(“固定関数パイプライン”)はオフになります。たとえば頂点プログラムを使用すると標準 3D 変換やライティング、テクスチャ座標生成は完全にオフにされます。同様にして、フラグメントプログラムを使用することで SetTexture コマンドで定義された texture combine モードを置き換えられるため、SetTexture コマンドは必要ありません。

頂点/フラグメントプログラムを書くことは 3D 変換、ライティング、およびテクスチャ座標空間に関する網羅的な知識が必要です。なぜなら OpenGL のような API に内蔵されている固定関数機能を自身で書き直す必要があるためです。一方で、ビルトインされている以上のことが実現できるようになります。

ShaderLab で Cg/HLSL の使用

ShaderLab のシェーダーは普通 Cg/HLSL プログラミング言語で書かれています。Cg と Dx9 スタイルの HLSL は一つの実用的な意図を持っており、同じ言語を使用しています。それゆえに Cg と HLSL を同じ意味で使用しています。(詳細はUnityで使用するシェーダー言語を参照してください)

シェーダーテキストは通常シェーダーテキストに「Cg/HLSL スニペット」を埋め込無事によって書かれています。スニペットは Unity エディターにより低レベルのシェーダーアセンブリにコンパイルされ、そしてゲームのデータファイルに含まれる最終シェーダーはこの低レベルのアセンブリしか含まれません。Project View でシェーダーを選択するとき、Inspector は Cg コンパイル後のシェーダーテキストを表示し、デバッグの手助けとなるかもしれません。Unity は自動的に Cg スニペットを Direct3D 9、OpenGL、Direct3D 11、OpenGL ES 等にコンパイルするため、シェーダーはすべてのプラットフォームで動作します。Cg/HLSL コードはエディターによりコンパイルされるため、ランタイムで Cg シェーダーを作成することはできません。

一般的に、スニペットは Pass ブロックに配置されます。次のように書きます。

Pass {
    // ... the usual pass state setup ...

    CGPROGRAM
    // compilation directives for this snippet, e.g.:
    #pragma vertex vert
    #pragma fragment frag

    // the Cg/HLSL code itself

    ENDCG
    // ... the rest of pass setup ...
}

次のサンプルでオブジェクトの法線をカラーとしてレンダリングする完全なシェーダーを示します。

Shader "Tutorial/Display Normals" {
    SubShader {
        Pass {

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR0;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.color = v.normal * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4 (i.color, 1);
            }
            ENDCG

        }
    }
}

オブジェクトに適用したとき、下の画像のようになります。

この “Display Normals” シェーダーにはプロパティーがなく、ひとつのパスを持ったサブシェーダーであり、Cg/HLSL コードを除いては空白です。一部のコードを分析してみます。

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// ...
ENDCG

スニペット全体は CGPROGRAMENDCG キーワードの間に書かれています。最初にコンパイルディレクティブが #pragma ステートメントとして与えられます。

  • #pragma vertex name により、与えられた関数でコードが頂点プログラムを含むこと (ここでは vert) を意味します。
  • #pragma fragment name により、、与えられた関数でコードがフラグメントプログラムを含むこと (ここでは frag) を意味します。

コンパイルディレクティブに続くのは、生の Cg/HLSL コードです。ビルトイン シェーダー includeファイル を含めるすることで始めます。

#include "UnityCG.cginc"

UnityCG.cginc ファイルは共通で使用される宣言と関数を含み、シェーダーを小さく維持できるようにしています(詳細については ビルトイン シェーダー includeファイル を参照してください)。ここでは、このファイルから appdata_base 構造を使用します。もちろん、シェーダーの中で定義して、ファイルを 内包しないことも可能です。

次に vertex to fragment 構造(ここでは __v2f__)、すなわちどの情報が頂点プログラムからフラグメントプログラムに渡されるか、を定義します。位置とカラーの引数を渡します。カラーは頂点プログラムで計算され、フラグメントプログラムに出力します。

次に頂点プログラム、ここでは vert 関数、を定義します。ここでは位置と入力された法線をカラーとして出力します。 o.color = v.normal * 0.5 + 0.5;

通常のコンポーネントは –1 から 1 の範囲にあり、カラーは 0 から 1 の範囲にあるため、上記コードの法線をスケールしたうえバイアスします。次にフラグメントプログラム、ここでは frag 関数を定義し、これは計算したカラーと 1 をアルファコンポーネントとして出力します。

fixed4 frag (v2f i) : SV_Target
{
    return fixed4 (i.color, 1);
}

これで完了であり、シェーダーは完成です! この簡単なシェーダーでもメッシュ法線を視覚化するのに便利です。

当然、このシェーダーはすべての光にまったく反応せず、そこでいろいろと面白くなります。詳細については サーフェイスシェーダー を参照してください。

Cg/HLSL コードでシェーダープロパティーを使用

シェーダーでプロパティーを定義するとき、_Color_MainTex という名前をつけます。Cg/HLSL で使用するためには、マッチングする名前と型で変数を定義するだけです。詳細は Cg/HLSL でシェーダープロパティー を参照してください。

カラーにより調整されたテクスチャを表示する、完全なシェーダーを次に示します。当然、texture combine コールでも同様のことを簡単に行うことができますが、ここでのポイントは Cg でのプロパティーを使用する方法を見ていくことです。

Shader "Tutorial/Textured Colored" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _MainTex ("Texture", 2D) = "white" { }
    }
    SubShader {
        Pass {

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        fixed4 _Color;
        sampler2D _MainTex;

        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };

        float4 _MainTex_ST;

        v2f vert (appdata_base v)
        {
            v2f o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 texcol = tex2D (_MainTex, i.uv);
            return texcol * _Color;
        }
        ENDCG

        }
    }
}

このシェーダーの構造は前述のサンプルと同じです。ここでは二つのプロパティー、すなわち _Color_MainTex を定義します。Cg/HLSL コードの中で対応する変数を定義します。

fixed4 _Color;
sampler2D _MainTex;

詳細については Cg/HLSL でシェーダープロパティー を参照してください。

ここでの頂点とフラグメントプログラムは何か素晴らしいことをしてくれるわけではありません。頂点プログラムは UnityCG.cginc から TRANSFORM_TEX マクロを使用してテクスチャスケールとオフセットが正しく適用されていることを確認し、フラグメントプログラムはテクスチャをサンプリングし、カラープロパティーに掛け合わせます。

要約

いくつかの簡単なステップで自分でシェーダープログラムを生成する方法を紹介しました。ここのサンプルはシンプルですが、自身の裁量で複雑なシェーダープログラムを書くことは問題ありません。これにより Unity を最大限に活用し、最適なレンダリング結果を得るための手助けとなります。

さらに詳しい ShaderLab リファレンスマニュアルは シェーダーリファレンス にあります。また 頂点とフラグメントシェーダーの例も用意しています。シェーダーに関するフォーラムが forum.unity3d.com にあるので、シェーダーのことで手助けが必要であれば行ってみてください。プログラミングとともに Unity と ShaderLab のパワーを楽しみながら味わってください。

シェーダー: ShaderLab と固定関数シェーダー
Physics