このチュートリアルでは自身で Unity シェーダーで頂点とフラグメントプログラムを書く方法を学びます。ShaderLab の基本の紹介については シェーダー: ShaderLab と固定関数シェーダー を参照してください。もしライティングに作用するシェーダーを書く場合は代わりに サーフェスシェーダーの記述 について読んでください。
最初にシェーダーの一般的な構造の復習から始めます。
Shader "MyShaderName"
{
Properties
{
// マテリアルプロパティーをここに記述する
}
SubShader // グラフィックスハードウェア A のサブシェーダー
{
Pass
{
//Pass コマンド ...
}
// 必要なばあいは、追加の Pass コマンド
}
// 必要なばあいは、追加のサブシェーダー
FallBack "VertexLit" // オプショナルな Fallback
}
Here at the end we introduce a new command: FallBack “VertexLit”. The Fallback command can be used at the end of the shader; it tells which shader should be used if no SubShaders from the current shader can run on user’s graphics hardware. The effect is the same as including all SubShaders from the fallback shader at the end. For example, if you were to write a fancy normal-mapped shader, then instead of writing a very basic non-normal-mapped subshader for old graphics cards you can just fallback to built-in VertexLit shader.
シェーダーの基本は、 シェーダー: ShaderLab と固定関数シェーダー で紹介しています。 また、プロパティー、サブシェーダー、 パス の完全なドキュメントも利用してください。
サブシェーダーを素早く作成する方法は、他のシェーダーで定義されたパスを使用することです。 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 のパワーを楽しみながら味わってください。