ここでは Unity シェーダーで 頂点とフラグメントプログラム の記述の基本を学びます。ShaderLab の基本は シェーダー - ShaderLab と固定関数シェーダー を参照してください。もしライティングに作用するシェーダーを書く場合は、代わりに サーフェスシェーダーの記述 を参照してください。
最初にシェーダーの一般的な構造から始めます。
Shader "MyShaderName"
{
Properties
{
// マテリアルプロパティーをここに記述する
}
SubShader // グラフィックスハードウェア A のサブシェーダー
{
Pass
{
//Pass コマンド ...
}
// 必要なばあいは、追加の Pass コマンド
}
// 必要なばあいは、追加のサブシェーダー
FallBack "VertexLit" // オプショナルな Fallback
}
最後に FallBack “VertexLit” という新しいコマンドを説明します。FallBack コマンドは、シェーダーの最後で使用されます。現在のシェーダーの SubShader (サブシェーダー) をユーザーのグラフィックスハードウェア上で実行できない場合に、どのシェーダーを使用するかを決定します。フォールバックシェーダーのすべてのサブシェーダーを最後に加える場合と同じ効果があります。例えば、法線マップを使った素晴らしいシェーダーを作成するとしましょう。次に、古いグラフィックスカードの非常に基本的で法線マップを使わないサブシェーダーを作成する代わりに、ビルトインの VertexLit シェーダーにフォールバックできます。
シェーダーの基本的な構成は シェーダー - ShaderLab と固定関数シェーダー を参照してください。また、Properties、SubShader、Pass などの詳しい説明も参照して下さい。
サブシェーダーを素早く作成するには、他のシェーダーで定義されたパスを使用します。コマンド UsePass はそれだけを行います。そのため、シェーダーコードを簡単に再利用することができます。例えば、以下のコマンドはビルトインの Specular シェーダーから “FORWARD” という名前のパスを使用します。 UsePass “Specular/FORWARD”
UsePass を機能させるには、使用したいパスに名前を与える必要があります。パス内の Name コマンドは、パスに名前を与えます。 Name “MyPassName”
最初のチュートリアル でひとつの texture combine コマンドだけを使用するパスを説明しました。次に自身のパスで頂点およびフラグメントプログラムを使用する方法をみていきます。
頂点およびフラグメントプログラムを使用するとき(いわゆる “プログラマブルパイプライン”)、グラフィックスカードにあるほとんどのハードコードされた機能(“固定関数パイプライン”)はオフになります。たとえば頂点プログラムを使用すると標準 3D 変換やライティング、テクスチャ座標生成は完全にオフにされます。同様にして、フラグメントプログラムを使用することで SetTexture コマンドで定義された texture combine モードを置き換えられるため、SetTexture コマンドは必要ありません。
頂点/フラグメントプログラムを書くことは 3D 変換、ライティング、およびテクスチャ座標空間に関する網羅的な知識が必要です。なぜなら OpenGL のような API に内蔵されている固定関数機能を自身で書き直す必要があるためです。一方で、ビルトインされている以上のことが実現できるようになります。
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 {
// ... 通常の Pass 状態の設定 ...
CGPROGRAM
// このスニペッツのコンパイルディレクティブ、例えば:
#pragma vertex vert
#pragma fragment frag
// Cg/HLSL コード自体
ENDCG
// ... 残りの Pass 設定 ...
}
次のサンプルでオブジェクトの法線をカラーとしてレンダリングする完全なシェーダーを示します。
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 = UnityObjectToClipPos(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
スニペット全体は CGPROGRAM と ENDCG キーワードの間に書かれています。最初にコンパイルディレクティブが #pragma ステートメントとして与えられます。
コンパイルディレクティブに続くのは、生の 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);
}
これで完了であり、シェーダーは完成です! この簡単なシェーダーでもメッシュ法線を視覚化するのに便利です。
当然、このシェーダーはすべての光にまったく反応せず、そこでいろいろと面白くなります。詳細については サーフェイスシェーダー を参照してください。
シェーダーでプロパティーを定義するとき、_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 = UnityObjectToClipPos(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 のパワーを楽しみながら味わってください。