こんにちは 長らくほったらかしだった ”うにばな”記事を再開したいと思います。
今回は要望が多めのシェーダに関するおはなしです。 といってもシェーダだけで分厚い本が何冊も書けるほどの情報量があるうえに進化し続けている分野ですので、とりあえずかいつまんで記事にしました。
非常に急いで書いたため すこし意味が通らなかったり説明が不十分なところがあるかと思いますが 今後も記事に手を入れていきますのでご容赦くださいませ。。UnityのShaderLabですが 基本的な記述はダイレクトXのHLSL またはOpenGLのGLSLどちらでも記述することが出来るという柔軟性のあるものです。3Dソフトなどでカスタムシェーダを使用することができるcgfxというシェーダもありますが 労せずシェーダを移植することは
可能ですので、開発時に3Dソフト上で確認しながらモデリングをすることも出来ると思います。学んだ知識がのちのち応用が効くという意味では
非常に開発者にとってはありがたいことです。
記事は3部にわけて
1.初めてのシェーダ (基礎知識 といってもちょっと難しい)
2.Unity公式シェーダリファレンスの意訳(必要なとこだけ)
3.シェーダを書いてみます(strumpyエディタ)など
時間があればイメージエフェクトなどもやっていきたいかな
(テクスチャレンダはプロ版のみなのでフィルタ形式のものはインディ版では難しいですが カメラの前にシェーダ入れた板ポリを置く貧乏シェーダは可能なんですよw)
今回はこちら↓を参考にシェーダの初歩的な知識について書かせていただきました
http://gameprogramming.me.land.to/PDF/ESL502.pdf
http://download.unity3d.com/support/resources/files/Unite08_Shaders.pdf
参考動画 チュートリアルなど
http://www.youtube.com/watch?v=37pc-eif24g&feature=plcp
http://www.youtube.com/user/JessyUV/videos?view=0
http://www.youtube.com/user/animate4evr

大まかにシェーダファイルに記述するシェーダは2つに分けられます。上手のようにGPUのvetexプロセッサに命令を送るのがvertexshader。Pixelプロセッサに命令を送るのがPixel シェーダです。
Vertex Shaders(頂点シェーダ)
- オブジェクト空間(ローカルな座標)からワールド座標へオブジェクトを変換します。
- 頂点ごとにどのようなデータも計算することができます。
- その他の入力データはすり抜けます。

Pixel Shaders(サーフェスシェーダ)
- 頂点シェーダから送られたデータを使用することができます。
- ピクセル単位の光源計算を行います
- 設定されたテクスチャを用いて最終的なピクセルの色を決定します

Tips: VertexシェーダもPixelシェーダもほぼ同様の関数で記述できるのですが、Pixel シェーダはデータ量が多いため vertexシェーダ内部にPixel シェーダの内容を移動して記述してしまえば 高速化が期待できます。
シェーダの記述
大まかにはC/C++とほとんど同じで、以下のような構成をしています。
シェーダの名前の定義
{
プロパティの定義
{
構造体の定義
グローバル変数の宣言
関数の定義
Subshader(一般的にはテクニック の定義)
{
パスの定義
…
}
}
}
シェーダの記述例
Shader "Tutorial/Textured Colored" { //シェーダファイル名の定義
Properties { //プロパティの定義(インスペクタに表示される)
_Color ("Main Color", Color) = (1,1,1,0.5)
_MainTex ("Texture", 2D) = "white" { }
}
SubShader { //サブシェーダの記述ここから
Pass {//パスの定義ここから
CGPROGRAM //Cgプログラムの開始
#pragma vertex vert //頂点シェーダの名前の定義 ここでは”vert”
#pragma fragment frag //サーフェスシェーダ(フラグメントシェーダ)の定義 ここでは”frag”
#include "UnityCG.cginc" //外部ファイルのインクルード”UnityCg.cginc”はUnityのシェーダの関数
float4 _Color; //float4型の”_Color”という名前の変数の定義(r,g,b,a)
sampler2D _MainTex; //sanpler2D型 テクスチャのUV値の定義
struct v2f { //構造体(いくつかの値をまとめて型として定義する)ここでは”v2”f型
float4 pos : SV_POSITION; //float4型の SV_POSITION型の宣言(頂点座標) ここでは"pos"
float2 uv : TEXCOORD0; //float2型の TEXCOORD型の宣言(テクスチャUV) ここでは”uv"
};
float4 _MainTex_ST;
v2f vert (appdata_base v) //※1
{
v2f o; //構造体v2f型のローカルな値”o"を定義
o.pos = mul (UNITY_MATRIX_MVP, v.vertex); //UNITY_MATRIX_MVP(現 行モデル*ビュー*射影行列)を用いて頂点座標の変換
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex); //※2
return o;
}
half4 frag (v2f i) : COLOR //サーフェスシェーダここから(戻り値COLOR型)
{
half4 texcol = tex2D (_MainTex, i.uv); //harf4型の変数”texcol"にテクスチャの色を設定
return texcol * _Color; //戻り値にtexcol (テクスチャの色)* _Color(マテリアルの色)を設定 いわゆるカラー乗算
}
ENDCG //Cgプログラムの終了
}
}
Fallback "VertexLit" //フォールバック関数(シェーダが実行できない時に呼ばれるシェーダ なるべく 標準的なシェーダを選択することで検索が早くなるようです)
}
※1: UnityCg.cgincファイルを開いて確認すると構造体 appdata_baseは
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
と定義されています。
※2: UnityCg.cgincファイルを開いて確認すると
// Transforms 2D UV by scale/bias property // テクスチャUVにスケールとバイアスを付与する関数です。
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
プロパティの定義
http://docs.unity3d.com/Documentation/Components/SL-Properties.html
シェーダの名前の後にプロパティというシェーダに与える値を宣言します。実際に型宣言はサブシェーダの中で行いますが、プロパティの宣言ではサブシェーダで設定する値それぞれに対応させてインスペクタ上での表示と最初に代入される初期値を設定します。
例:_Color ("Main Color", Color) = (1,1,1,0.5)
使用する値の名前(”インスペクタ上の表示の名前 ”,インスペクタ上のエディット表示の種類)=(設定する値の初期値)
name ("display name", Color) = (number,number,number,number)
例:
Shader "VertexLit" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0.5)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
}
}
SubShaderの定義(一般的にはテクニックと呼ばれる の定義)
SubShaderとは、Cg関数で書かれたレンダリング方法を記述するパスを複数まとめたものです。
1つのファイルに複数のSubShaderを定義することができます。またSubShader内には、複数 のパスを定義することができます。
Shader "MyShaderName" {
Properties {
... プロパティを書きます...
}
SubShader {
... サブシェーダーコマンドを記述 ...
Pass {
... Passコマンドを定義...
}
... 必要ならば続けてパスを記述...
}
SubShader {
... サブシェーダーコマンドを記述...
}
...フォールバックオプションを記述...
FallBack "VertexLit"
}
パスの定義
パスとは、バーテックスシェーダー(vertex)とピクセルシェーダー(fragment)をセットにしたものです。
CGPROGRAM …ENDCG の間で#pragma 名前でバーテックスシェーダーとピクセルシェーダーが最初に実行する関数とバージョンを指定します。 パス内部でシェーダーのメインとなる関数名とバージョ ンを設定するものです。
Pass {
...Pass の初期設定...
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
…Cg コードを記述します
ENDCG
}
関数の定義
HLSLでは、C/C++と同様に、関数を定義し呼び出すことができます。定義方法も同じです。
戻り値の型関数名(引数の型変数名[ : セマンティクス],
引数の型変数名[ : セマンティクス], … ) [ 戻り値のセマンティクス]
{
(関数の処理)
…
return 戻り値;
}
関数内で別の関数を呼び出すことや、別ファイルで定義されている関数をインクルードして使うこと
もできます。
#include "UnityCG.cginc"
windowsの場合 Unityインストールパス /Data/ CGIncludes / UnityCG.cginc
Macの場合 /Applications/Unity/Unity.app/Contents/CGIncludes/UnityCG.cginc
"UnityCG.cginc"ファイルに記述された関数はビルトイン関数と呼ばれます。
例:
float4 _Time (t/20, t, t*2, t*3) //時間を返す関数
float4 _SinTime (t/8, t/4, t/2, t) //サインあたりの時間を返す関数
float4 _CosTime (t/8, t/4, t/2, t) //コサイン・・
_ModelLightColor[N] (モデルのマテリアルカラー*ライトカラー)
_SpecularLightColor[N] (モデルのスペキュラカラー*ライトカラー)
PositionFog() //頂点のフォグの値を計算
ObjSpaceLightDir() //モデルを中心としてライトの方向を計算
ObjSpaceViewDir() //モデルを中心としてビュー方向(カメラの方向)を計算
TRANSFORM_TEX() //テクスチャの移動などの変換
DifuseLight() or SpecularLight() //ライブラリにない?
型
HLSLでは、C/C++と同様に、関数内のローカル変数やシェーダー全体で参照できるグローバル変数、
構造体、配列を宣言できます。宣言方法も同じで、以下のように行います。
型名変数名
標準のHLSLでは、変数の型として以下のものを使用できます。
bool - true またはfalse
int - 32 ビット符号付き整数
uint - 32 ビット符号なし整数
float - 32 ビット浮動小数点値
●float:高精度浮動小数点。一般的に32ビット、普通のプログラミング言語のfloat型。
●half:培地精度浮動小数点。精度は-60000から60000 3.3桁の範囲で、一般的に16ビット。
●fixed:低精度の固定小数点。-2.0から2.0の範囲と256分1の精度で、一般的に11ビット。
double - 64 ビット浮動小数点値
sampler2D-uv座標
samplerCUBE-cubeマップ座標
uniform−入力専用の定数データ
ベクトルは、vectorもしくは型名のあとに数字をつけることによって表現します。4次元ベクトルま でサポートしているので、数字は1〜4の範囲で指定します。
int2 pos2d; // int 型の2次元ベクトル
float3 pos3d; // float 型の3次元ベクトル
vector <double, 4> vec4; // double型の4次元ベクトル
各メンバは”x, y, z, w”または”r, g, b, a”を組み合わせることによりアクセスします。
pos.xyz pos.xy pos.xz pos.x; col.rgb col.rg col.gb col.a;
x〜w、r〜aは自由に組み合わせて記述できます。
行列は、最大4×4まで定義できます。行と列の数が異なる行列も扱うことができます。 matrixまたは 型名のあとに ”行数x列数” で宣言します。
int3x2 mat_i; int 型3行2列
float4x4 mat_f; float 型4行4列
matrix <double, 3, 3> mat_d; double型3行3列
行列のメンバへのアクセスは、メンバ名を指定することにより行えます。
●4×4の行列の場合
_m00, _m01, _m02, _m03
_m10, _m11, _m12, _m13
_m20, _m21, _m22, _m23
_m30, _m31, _m32, _m33
ほかに
_11, _12, _13, _14
_21, _22, _23, _24
_31, _32, _33, _34
_41, _42, _43, _44
どちらを使用しても良いです
制御文
HLSLでは、プログラムの流れを制御する「フロー制御文」も使用できます。DirectX9.0では、 if-else, for, while, do-while文を使うことができます。記述方法もC/C++とまったく同じです。 ただし、brake, countinue, (switch-case文)は、DirectX10以降でなければ使用できません。
セマンティクス
シェーダーが最初に実行する関数の引数と構造体のメンバに、変数の使用目的を示した「セマンティ クス」という文字列を指定できます。 関数が呼び出される際、頂点またはピクセルの情報は、該当するセマンティクスが付いた変数に渡されます。これにより、頂点またはピクセル情報の中から必要なものだけを受け取ることができます。なお、頂点にもピクセル情報にも含まれていないセマンティクスが付いている変数は、0が格納されます。また、シェーダーが最初に実行する関数の引数すべてに、セマンティクスを指定する必要があります。
float4 : POSITION //頂点座標(x,y,z,w)※wはスケールの値
float4 : TANGENT //ノーマルマップのタンジェント座標
float4: NORMAL; //頂点法線(x,y,z,w)
float4 : TEXCOORD0 //テクスチャ座標(UV)
float4: TEXCOORD1// テクスチャ座標2つ目(テクスチャUVはXBOX360では1メッシュあたり7つまでサポートしている?ようです)
float4 : COLOR //カラーの値(r,g,b,a)
一部ですが 上記のものがあります
組み込み関数一覧
HLSL標準関数の表ですがUnityのシェーダ上でのすべての関数についての検証はしていません。シェーダエディターなどで関数の機能が分からない時に対応表として使用することは出来ると思います。
http://msdn.microsoft.com/ja-jp/library/bb509611(v=vs.85).aspx
