ホーム < ゲームつくろー! < Unity/Shader編

Shader編
その2 ShaderLabでUnityシェーダの下地作り


 Unityには「固定機能シェーダ」「サーフェイスシェーダ」そして「頂点/フラグメントシェーダ」という3種類のシェーダタイプがあり、そのどれもがShaderLabというUnity独自のフォーマット下で記述されます。

 ShaderLabのリファレンスはこちらになります:
Unity.ShaderLab sintax: http://docs.unity3d.com/Documentation/Components/SL-Shader.html



@ ShaderLabの役割

 ShaderLabはUnityとシェーダコードとの仲立ちをするコードです。シェーダは何か入力情報が無いと動かす事ができませんが、その「入力情報」はUnityのGUIやスクリプトで与えます。マテリアルをモデルに適用した時にテクスチャが設定されていたりしますが、あれは実はShaderLabに記述されているからUnityが認識出来ているんです。

 ShaderLabのざっくりとしたフォーマットは次の通りです:

Shader "MyShader" {
    Properties {
        //...
    }
    SubShader {
        //...
    }
    SubShader {
        //...
    }
    SubShader...

    FallBack "Diffuse"
}

 大きく分けて「Shader枠」「Properties枠」そして「SubShader枠」があります。

 Shader枠はシェーダを記述する大外の枠組みです。"MyShader"と書かれている部分にそのシェーダの名前を記述します。例えば特殊なDiffuseライティングをするシェーダなら"SpecialDiffuse"とか。ここで記述した名前がUnityのマテリアルにセットするシェーダ名となります(例:Custom/SpecialDiffuse)。

 Properties枠には、Unity側からシェーダに渡したい値を宣言します。これについては後ほど。

 SubShader枠にシェーダ本体を記述します。一つのSubShaderに1本分のシェーダコードが入ります。んじゃ、なぜSubShaderが複数あるのか?これはUnityが「そのハードウェアにあったシェーダを選択してくれるから」なんです。Unityはマルチプラットフォームに対応したゲームエンジンです。しかし、あらゆる差異を吸収してくれるわけではなくて、シェーダについては各プラットフォームで動く物をそれぞれ記述しなければならず、それを上のSubShader枠に書くわけです。すると、Unityは上から順にシェーダをチェックしてくれて、動くシェーダを見つけたらそれを適用してくれます。

 じゃぁ、最後までチェックして見つからなかったらどうするのか?その為にあるのが「FallBack」です。ここにはどのプラットフォームでも動作する最終シェーダを指定します。"Diffuse"と指定するとほぼ確実に動きます。ただし、見た目はもちろん最悪になります。でもゲームが動かなくなるよりはマシです。

 すべてのUnityシェーダはこのフォーマットで書かれます。ではまずProperties枠について見ていきましょう:



A Properties枠でUnityとコミュニケーション

 Properties枠にはUnity側から渡したい値を宣言します。これには次のような7タイプがあります:

nameInShader ("display name", Range (min, max)) = number
nameInShader ("display name", Color) = (number,number,number,number)
nameInShader ("display name", 2D) = "name" { options }
nameInShader ("display name", Rect) = "name" { options }
nameInShader ("display name", Cube) = "name" { options }
nameInShader ("display name", Float) = number
nameInShader ("display name", Vector) = (number,number,number,number)

nameInShaderという所にはシェーダ内で使用する変数名を記述します。一方"display name"にはUnityのInspectorに表示される名前を入れます。これは見た目だけのお話なので、コード内で直接参照する事はありません。その後ろにある「Range」とか「Color」というのは変数の型です。つまりUnityのシェーダでは上の7タイプの型を扱えるという事になります。各型には初期値を設ける事ができて、それがイコールの右側に書かれている記述です。

 各型について簡単に説明します:

○ Range

 範囲が決まっている浮動小数点値を使いたい場合にこの型を使います。min, maxにそれぞれ最小値、最大値を指定します。numberにはその範囲内の初期値を入れます。Unity側のマテリアルのInspectorにはスライダーが付属するようになります:


○ Color

 Unity側で色を指定したい場合に用います。これを指定するとUnity側にカラーピッカーが付くようになります:

○ 2D、Rect、Cube

 これらはいずれもテクスチャの指定です。2Dは2のべき乗サイズのテクスチャ、Rectは2のべき乗以外のテクスチャ、そしてCubeはキューブテクスチャをそれぞれ表します。イコールの右側にある"name"には、テクスチャが指定されていない時のデフォルトテクスチャ名を付けます。これはUnity側で指定されていて「white」「black」「gray」「bump」のいずれかになります。そういう色のテクスチャになるという事です。Unity側のInspectorにはこんなのが付くようになります:

○ Float、Vector

 Floatは1変数、Vectorは4変数の値をそれぞれ表します。Inspectorはこんなです:

こんな感じでProperties枠に型を指定すると、Unityとコミュニケーションがとれるようになるわけです。実装例はこうなります:

Properties {
    _Tex ("Tex2D", 2D) = "white" {}
    _TexRect ("TexRect", Rect) = "white" {}
    _TexCube ("TexCube", Cube) = "white" {}
    _Rabge( "Range", Range(0.0, 1.0) ) = 0.5
    _Color ("Color", Color) = (0.5, 1.0, 0.1, 0.7)
    _Float("Float", Float) = 20.0
    _Vector("Vector", Vector) = (0.5, 0.7, 0.2, 0.4)
}

では、Unity側で渡された値をシェーダで使う部分を次に見て行きましょう。



B SubShader枠のPass

 SubShader枠にはシェーダを実際に記述します。ただ、ここにはシェーダとは別の記述も色々と入ってきます。その一つが「Pass」です。HLSLを書いた事がある人なら「technique内に記述するレンダーステートです」と言えば「あぁ〜」と納得してもらえると思います。ただ、Unity専用の物もあります。

 PassはSubShaderの中に「Pass」という枠を設けて、そこに記述します:

SubShader {
    Pass {
        ....
    }
}

実は固定機能シェーダはこのPassのみで指定するシェーダになります。DirectX9の固定機能のような物というわけです。Pass内に記述できる物は非常に沢山ありますので、ざざっと説明するに留めます:

項目 書き方例 意味
色、材質、ライト周り
Color Color (0.2, 0.5, 0.1, 1.0)
Color[_ColorInProperties]
ポリゴンの地の色(RGBA)。PropertiesのColorを名前指定できる。
Material Material {
    Diffuse (1.0, 0.5, 0.7, 1.0 )
   Ambient (1.0, 0.5, 0.7, 1.0 )
   Specular (1.0, 1.0, 1.0, 1.0 )
    Shininess 0.3
    Emission [_Emission]
}
ポリゴンのライティング属性。Shininessは0〜1の間で、0にすると広範囲なハイライトになる。1にするとハイライトが点のようになる(ツルツルになる感じ)。
全部設定する必要は無くて、デフォルトは0カラーになる。
Colorと同じようにPropertiesの変数名を名前指定できる。
Lighting Lighting On ライトのオンオフを指定。オンは「On」、オフは「Off」。OffにするとMaterialの設定は無効になってColorで指定した地の色がそのまんま出る。
SeparateSpecular SeparateSpecular On Onにするとスペキュラ効果がシェーダの最後に適用されるようになる。Offにするとテクスチャを貼り付ける時にスペキュラ効果が計算。
ColorMaterial ColorMaterial AmbientAndDiffuse 頂点カラーが設定されている場合に、マテリアルで指定されている物の代わりにそれを使う。
AmbientAndDiffuse: AmbientとDiffuseを頂点カラーで指定
Emission: Emissionを頂点カラーで指定
カリング、深度テスト
Cull Cull Back カリング指定
Back: 背面カリング。普通これ
Front: 前面カリング。裏表があべこべになる
Off: カリングしない。両面描画
ZWrite ZWrite On 深度バッファへの書き込み許可。On/Off
ZTest ZTest LEqual Zテスト。合格すると画面に点が穿たれる。デフォルトはLEqual
Less: 深度が未満なら合格
LEqual: 深度が以下なら合格
GEqual: 深度が以上なら合格
Equal: 同じ値なら合格
NotEqual: 違う値なら合格
Always: いっつも合格
Offset Offset 0, -1 書きこむ深度値をずらす時に使用。1番目の数値(Factor)は深度の基準点、2番目(Unit)はそこからどのくらいずらすか。深度バッファシャドウとかに使うのかな…
テクスチャ結合
SetTexture SetTexture[_MainTex] {
    combine texture
}
古き良きマルチテクスチャブレンド。次のコマンドが指定できる:
・combine src1 * src2 (乗算合成)
・combine src1 + src2 (加算合成)
・combine src1 - src2 (減算合成)
・combine src1 +- src2 (足した後に0.5引く)
・combine src1 lerp (src2) src3 (1と3を2の値で補間)
・combine src1 * src2 + src3 (1に2のα掛けて3を足す)
・combine src1 * src2 +- src3 (1に2のα掛けて3を符号付きで足す)
・combine src1 * src2 - src3 (1に2のα掛けて3を引く)

srcには次:
・Previous (前のSetTextureの結果を使う)
・Primary (一番最初のライティングもしくは頂点カラーを使う)
・Texture (SetTextureで指定したテクスチャを使う)
・Constant (定数カラーを使う)

Modifiersというオプション(Double, Quad)も付けられる

ConstantColor (1.0, 0.5, 0.7, 1.0)で定数カラーを指定
フォグ
Fog Fog {
    Mode Linear
    Color (0.3, 0.3, 0.3, 1.0)
    Density 1.0
    Range 100.0, 1000.0
}
フォグ(霧)の指定。コマンドは次の通り:
・Mode
Global 試したんですが良く分かりません(T-T)(求む情報!)
(2013. 2. 22追記)
Globalの値はEdit→Render Settingsにある
Fogの設定に従うという事のようです。
ご情報を頂きましたWLKさん、ありがとうございますー!

Linear カメラの位置から直線的に色が付いていくフォグ
Exp 指数関数的にかかるフォグ
Exp2 指数関数的にかかるフォグ。指数がべき乗。
・ Color
フォグの色
・ Density
ExpとExp2の時のフォグの掛かり具合。値が大きくなるほど近い距離で急激にフォグがかかる。
・ Range
Linearの時の掛かり始める距離と100%かかる距離を指定
αテスト
AlphaTest AlphaTest Greater 0.1 いわゆる「パンチ抜き」を行うかのテスト。デフォルトはOff。テストを行う場合は以下のフラグと比較する値を併記する:
Greater: 比較値より大きければ合格(ピクセルを穿つ)
GEqual: 比較値以上ならば合格
Less: 比較値未満なら合格
LEqual: 比較値以下なら合格
Equal: 比較値と同じなら合格
NotEqual: 違う値なら合格
Always: いっつも合格
Never: いっつも不合格
ブレンディング
Blend Blend SrcAlpha OneMinusSrcAlpha
Blend One Zero, One Zero
バックバッファとのブレンド方法を指定。極めて重要。
指定できるフラグは以下の通り:
・One (100%)
・Zero (0%)
・SrcColor (塗ろうとする色)
・SrcAlpha (塗ろうとするα値)
・DstColor (下地の色)
・DstAlpha (下地のα値)
・OneMinusSrcColor (塗ろうとする色の補色: 1-SrcColor)
・OneMinusSrcAlpha (塗ろうとするα値の補数: 1-SrcAlpha)
・OneMinusDstColor (下地の色の補色: 1-DstColor)
・OneMinusDstAlpha (下地のα値の補数: 1-DstAlpha)
BlendOp BlendOp Sub ブレンドする時の符号。指定しないと足し算になる。
・Min 色の値の小さい方を採用
・Max 色の値の大きい方を採用
・Sub 減算
・RevSub 減算して反転
Pass内タグ
Tags Tags {
    "LightMode" = "PrepassBase"
    "RequireOptions" = "SoftVegetation"
}
Passの結果をUnityのライティングパイプラインのどこにどう適用させるかを決める。
これ…色々わからない(T-T)

○ LightMode
・Always
Unityのライティング効果を適用しない
・ForwardBase
Forward renderingの頂点ライティングを適用
・ForwardAdd
Forward renderingのピクセルライティングを適用
・PrepassBase
Deffered Lightingの法線とスペキュラ強度をレンダリング
・PrepassFinal
Deffered Lightingでテクスチャ、ライティング、エミッションを適用して描画
・Vertex
オブジェクトがライトマップでない時に頂点ライティングを適用
・VertexLMRGBM
オブジェクトがライトマップの時に頂点ライティングを適用。
ライトマップはRGBMにエンコードされる
・VertexLM
オブジェクトがライトマップの時に頂点ライティングを適用。
ライトマップはduble-LDRにエンコードされる(古いハード用)
・ShadowCaster
Shadow casterとしてオブジェクトを描画
・ShadowCollector
Forward Renderingでスクリーンスペース内の影を集約する

○ RequireOptions
・SoftVegetation
UnityのQuality SettingsがSoftVegetationの時だけ描画。
名前
Name Name hogePass パスに名前を付ける
入力セマンティクスバインド
BindChannels BindChannels {
    Bind "Vertex", vertex
    Bind "texcoord1", texcoord1
}
Unityから下りてくる頂点シェーダの入力セマンティクスを特定のセマンティクスにマップしたい時に使う:
Bind "source", target の形でバインド

source
・Vertex (頂点座標)
・Normal (法線ベクトル)
・Tangent (接線ベクトル)
・Texcoord (プライマリUV)
・Texcoord1 (セカンダリUV)
・Color (頂点カラー)

○ target
・Vertex (頂点座標)
・Normal (法線ベクトル)
・Tangent (接線ベクトル)
・Texcoord (全テクスチャステージのUV)
・Texcoord1...n (指定番号のテクスチャステージのUV)
・Color (頂点カラー)

おおよそこんな感じです。

 SubShader自身に付けるTagsがあります。特に"Queue"がとても重要です:

項目 書き方例 意味
Tags Tags {
    "Queue" = "Transparent"
    "RenderType" = "Opaque"
    "Projector" = "True"
}
Queue 描画のプライオリティを指定。透過がある物は特にこれを指定しないとまずいです。

・Background
あらゆる描画の前に描画。背景やスカイボックスなどはこれにするべき。
・Geometry(デフォルト)
特にプライオリティを付けない。通常描画に従う。
・AlphaTest
αテスト用のジオメトリとして使用。
・Transparent
GeometryとAlphaTestの後にBack to Front順で描画する。透過オブジェクトはこれにしないとうまく描画されない。
・Overlay
すべての描画の後に描画される。スクリーンに上書きする系の描画はこれで。
RenderType SubShaderがどういう性質の描画をするかを特定のシェーダに伝える。
わからん(^-^; 列挙だけします:

・Opaque (ソリッド(不透明)です)
・Transparent (透過してます)
・TransparentCutout (透過してます)
・Background (スカイボックスなどの背景描画用のシェーダです)
・Overlay (スクリーンに上書き用のシェーダです)
・TreeOpaque (木の幹を描画するためのシェーダです)
・TreeTransparentCutout (木の葉を描画するためのシェーダです)
・TreeBillboard (ビルボードな木を描画するシェーダです)
・Grass (草を描画する為のシェーダです)
・GrassBillboard (ビルボードな草を描画するためのシェーダです)
IgnoreProjector 投影効果(Projectors)の影響を受けるかどうかを指定(True)



C SubShader枠のシェーダ

 Pass長かった…orz。

 SubShader枠内にシェーダを記述するのはサーフェイスシェーダ、もしくは頂点/フラグメントシェーダの時だけです。詳しくは前章を。どちらもHLSLもしくはCgで記述するのですが、その時にUnityに色々教えてあげる必要があります。それらについては、サーフェイスシェーダもしくは頂点/フラグメントシェーダで決まっていますので、次の章で分けて説明します。