このチュートリアルでは自身でUnity シェーダで頂点およびフラグメント プログラムを書く方法を学びます。 ShaderLab の基本の紹介については 初心者向けのチュートリアル を参照下さい。もしライティングに作用するシェーダを書く場合は代わりに Surface Shaders について読んで下さい。
最初にシェーダの一般的な構造の復習から始めます:
Shader "MyShaderName" {
Properties {
// ... properties here ...
}
SubShader {
// ... subshader for graphics hardware A ...
Pass {
// ... pass commands ...
}
// ... more passes if needed ...
}
SubShader {
// ... subshader for graphics hardware B ...
}
// ... Optional fallback ...
FallBack "VertexLit"
}
最後に新しいコマンドを紹介します: FallBack “VertexLit”
Fallback コマンドはシェーダの最後に使用できます。もし現在のシェーダからどの SubShaders もユーザのグラフィックス ハードウェア で動作しない場合にどのシェーダを使用すべきか指示します。効果としては,全てのFallbackシェーダからのサブシェーダを全て最後に記述するのと同じです。例えば,もし法線マッピング シェーダを書く場合に,古いグラフィックスカードのために基本的な 非法線マッピングのサブシェーダを記述する代わりに,内蔵 VertexLit シェーダに Fallback するだけです。
基本的なシェーダの構成要素は シェーダチュートリアル入門 で紹介されていますが, Property, SubShader および Pass の完全なドキュメントもまた利用可能です。
サブシェーダ を構築する早い方法は他のシェーダで定義されたパスを使用することです。UsePass コマンドはまさにそれをしていて,シェーダコードをスマートに再利用できます。サンプルとして,次のコードは内蔵 Specular シェーダの “BASE” という名前のパスを使用します: UsePass “Specular/BASE”
UsePass が動作するためには,使用したいパスに名前をつける必要があります。パスの中の Name コマンドにより名前がつけられます: Name “MyPassName”
最初のチュートリアル でひとつの texture combine コマンドだけを使用するパスを説明しました。次に自身のパスで頂点およびフラグメント プログラムを使用する方法をみていきます。
頂点およびフラグメント プログラムを使用するとき(いわゆる “プログラマブル パイプライン”),グラフィックスカードにあるほとんどのハードコードされた機能(“固定関数パイプライン”)はオフになります。たとえば頂点プログラムを使用すると標準3D変換,ライティング,およびテクスチャ座標生成は完全にオフにされます。同様にして,フラグメント プログラムを使用することで SetTexture コマンドで定義された texture combine モードを置き換えられるため, SetTexture コマンドは必要ありません。
頂点/フラグメント プログラムを書くことは3D変換,ライティング,およびテクスチャ座標空間に関する網羅的な知識が必要です。なぜならOpenGLのようなAPIに内蔵されている固定関数機能を自身で書き直す必要があるためです。一方で,ビルトインされている以上のことが実現できるようになります。
ShaderLabのシェーダは通常シェーダテキストに “Cg スニペット” (短いコード) を埋め込んで Cg プログラミング言語 で書かれています。Cgスニペットは Unity エディタにより 低水準シェーダアセンブリにコンパイルされ,そしてゲームのデータファイルに含まれる最終シェーダはこの低水準アセンブリしか含まれません。Project View でシェーダを選択するとき,Inspector は Cg コンパイル後の シェーダテキストを表示し,デバッグの手助けとなるかもしれません。Unity は自動的に Cg スニペットをDirect3D, OpenGL, Flash, その他にコンパイルするため,シェーダは全てのプラットフォームで動作します。Cg コードはエディタによりコンパイルされるため,ランタイムで Cg シェーダを作成することは出来ません。
一般的には,Cg スニペットは Pass ブロックに配置されます。次のように書きます:
Pass {
// ... the usual pass state setup ...
CGPROGRAM
// compilation directives for this snippet, e.g.:
#pragma vertex vert
#pragma fragment frag
// the Cg code itself
ENDCG
// ... the rest of pass setup ...
}
次のサンプルでオブジェクトの法線をカラーとしてレンダリングする Cg プログラムの完全なシェーダを示します:
Shader "Tutorial/Display Normals" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float3 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;
}
half4 frag (v2f i) : COLOR
{
return half4 (i.color, 1);
}
ENDCG
}
}
Fallback "VertexLit"
}
オブジェクトに適用したとき,次のような画像が結果として得られます(当然グラフィックスカードが頂点およびフラグメントシェーダをサポートしている場合にかぎります):
この“Display Normals” シェーダにはプロパティがなく,ひとつのパスを持った,ひとつのサブシェーダであり,Cg コードを除いて空白です。最後に 内蔵 VertexLit シェーダへの Fallback が定義されています。Cg コードをひとつづつ解析していきます:
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// ... snip ...
ENDCG
Cg スニペット全体はCGPROGRAM および ENDCGキーワードの間に書かれています。最初にコンパイルディレクティブが #pragma ステートメントとして与えられます:
#pragma vertex name
により,与えられた関数でコードが頂点プログラムを含むこと (ここでは vert
) を意味します。#pragma fragment name
により,,与えられた関数でコードがフラグメントプログラムを含むこと (ここでは frag
) を意味します。コンパイルディレクティブに続くのは,生のCg コードです。内蔵 Cg ファイル を include することで始めます。
#include "UnityCg.cginc"
UnityCg.cginc ファイルは共通で使用される宣言は関数を含み,シェーダが小さく維持できるようにしています(詳細についてはシェーダ include ファイル を参照して下さい)。ここでは,このファイルから appdata_base を使用します。もちろん,シェーダの中で定義して,ファイルを include しないことも出来ます。
次に “vertex to fragment” 構造(ここでは v2f ),すなわちどの情報が頂点プログラムからフラグメントプログラムに渡されるか,を定義します。位置およびカラーの引数を渡します。カラーは頂点プログラムで計算され,フラグメントプログラムに出力します。
次に頂点プログラム,ここでは vert 関数,を定義します。ここでは位置および入力された法線をカラーとして出力します: o.color = v.normal * 0.5 + 0.5;
通常のコンポーネントは –1 から 1 の範囲にあり,カラーは 0 から 1 範囲にあるため,上記コードの法線をスケールしたうえバイアスします。次にフラグメントプログラム,ここでは frag 関数を定義し,これは計算したカラーおよび 1 をアルファ コンポーネントとして出力します。
half4 frag (v2f i) : COLOR
{
return half4 (i.color, 1);
}
これで完了であり,シェーダは完成です! この簡単なシェーダでもメッシュ法線を視覚化するのに便利です。
当然,このシェーダは全ての光に全く反応せず,そこで色々と面白くなります。詳細については サーフェイス シェーダ を参照して下さい。
シェーダでプロパティを定義するとき,_Color または _MainTex という名前をつけます。Cg で使用するためには,マッチングする名前と型で変数を定義するだけです。 Unity は自動的にシェーダプロパティとマッチングした名前の Cg 変数をセットします。
カラーにより調整されたテクスチャを表示する,完全なシェーダを次に示します。当然,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"
float4 _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;
}
half4 frag (v2f i) : COLOR
{
half4 texcol = tex2D (_MainTex, i.uv);
return texcol * _Color;
}
ENDCG
}
}
Fallback "VertexLit"
}
このシェーダの構造は前述のサンプルと同じです。ここでは二つのプロパティ,すなわち_Color および _MainTex を定義します。 Cg コードの中で対応する変数を定義します:
float4 _Color;
sampler2D _MainTex;
詳細については Cg でシェーダプロパティをアクセスする を参照して下さい。
ここでの頂点およびフラグメントプログラムは何か素晴らしいことをしてくれるわけではありません。頂点プログラムは UnityCG.cginc から TRANSFORM_TEX マクロを使用してテクスチャ スケールおよびオフセットが正しく適用されていることを確認し,フラグメントプログラムはテクスチャをサンプリングし,カラープロパティに掛け合わせます。
ここでは自身のフラグメントプログラムを記述しているため, SetTexture コマンドが必要ないことに留意して下さい。テクスチャがシェーダにどう適用されているか完全にフラグメントプログラムにより制御されています。
いくつかの簡単なステップで自身のシェーダプログラムを生成する方法をみてきました。ここのサンプルはシンプルですが,自身の裁量で複雑なシェーダプログラムを書くことは問題ありません。これにより Unity を完全に活用して最適なレンダリング結果が得られます。
完全な ShaderLab リファレンスマニュアルは ここ にあります。シェーダのためのフォーラムは forum.unity3d.com にあるので,シェーダのことで手助けが必要であれば行ってみて下さい。プログラミングとともに Unity および ShaderLab のパワーを楽しく味わって下さい!