積木製作, Unity, シェーダー, チュートリアル, 頂点,フラグメント

Unityシェーダーチュートリアル  Vertex/Fragment Shader の基本

シェーダー

今回はもう一つの記述形式である Vertex/Fragment Shader について解説していきます。

字が多めなので頑張って下さい。

 

Unityにおける Surface ShaderVertex/Fragment Shader の大まかな使い分けは、

ライトの影響を受けるモノには Surface Shader で、受けないモノには Vertex/Fragment Shader です。

 

が、勘違いしてはいけません。「Surface Shader > Vertex/Fragment Shader」ではなく、

むしろシェーダーの基本は Vertex/Fragment の方です。(以降 V/F Shader と書きます)

 

V/F Shader でもライトやリフレクションプローブの影響を与えることは可能です。

と言うより、Unityの内部的に Surface Shader は全て V/F Shader に自動変換されています。

 

様々な環境の影響を受けるシェーダーは、それなりの知識を持ってゴリゴリと書く必要があり、

とてもシェーダー初心者に太刀打ちできるものではありません。

そこで、初心者でも比較的簡単にシェーダーが作れるように、

Surface Shader という仕組みが用意されています。

 

筆者も、エフェクトやUI,簡単なポストプロセスを作りたい時に、

ちょびっと V/F Shader を使う程度です。

 

 Unityにおける V/F Shader の基本構造

プロジェクトウィンドウでシェーダーを新規作成する際に、Unlit Shader を選択すると

V/F Shader の雛型が出来上がります。

ただし、この雛型コードの中にはフォグの影響やUVのオプションが最初から組み込まれていて、

少しだけ難しくなっています。

 

可能な限りミニマムな方が分かりやすいと思うので、

それらのオプションを外して、最軽量な状態の V/F Shader から見てみましょう。

(投稿時に使用した Unity のバージョンは 5.6.0f3)

 

 Pass

まず最初に Surface Shader と違うところは、

CGPROGRAM ~ ENDCG を Pass { } で囲んでいるところです。

囲まなければエラーになります。何故と思わずとりあえず囲んでおきましょう。

 

 #pragma vertex / #pragma fragment

Surface Shader の時は、こうでした。


#pragma surface surf Standard

 

V/F Shader の時は、こう書きます。


#pragma vertex vert

#pragma fragment frag

Vertexシェーダー に vert関数を、

Fragmentシェーダー に frag関数を使いますよ、と言う指定です。

 

コードの下の方を見ると vert 関数と frag 関数が記述されています。

(勿論、vert frag の名前を変えても問題ありません。)

 

 V/F Shader の処理の流れ

だいぶ語弊があるかもしれませんが、V/F Shader の流れをかなりざっくり言うとこんな感じです。

 

appdataモデルが持っているどの情報を使うのかを指定。

v2f描画のために必要な情報を指定。

vertv2fで指定した情報を計算・確定。

fragvertで確定したv2fの情報をもとにピクセル描画

 

簡単で直観的に伝えたいのですが、なかなか難しい・・・

今回のミニマム V/F Shader で例えるとこんな感じ?

 

appdataモデルの頂点座標を参照するよ。

v2fOK。頂点座標だね。準備は良いかい、vert君?

vertなるほど。カメラから見た時の画面上の頂点位置はここだぜ。

fragよしきた。じゃぁ、その中を塗りつぶすよ。

 

みたいな・・・

 

 appdata

描画にあたり、モデルのどの情報が必要なのかを指定します。

今回のミニマムコードでもそうですが、頂点座標は必須です。

描画したいのに頂点が無いのでは話になりません。


struct appdata {

float4 vertex : POSITION;

};

 

: POSITION が頂点座標だよ、という意味で、(セマンティクスと呼ばれています)

vertex がそれを格納する変数です。(絶対にfloat4)

セマンティクス名は自由に変えられません。座標を取得したい場合は絶対に : POSITION です。

変数名は何でも良いです。

 

ちなみに Unity4 の時のデフォルトはこうでした。


struct appdata {

float4 pos : POSITION;

};

 

頂点座標以外にもUVとか法線とかいろいろと取得できますが、それはまた後日。

 

 v2f

v2f と言うのは、 vertex から fragment へ という意味でしょう。きっと。

描画時に必要な情報を指定します。

ほとんどの場合、appdata の中身と同じ感じになります。「Aだよ」「Aだね」みたいな。


struct v2f {

float4 vertex : SV_POSITION;

};

 

appdata と同様に、頂点座標の指定は必須です。

ただし、ここでは : SV_POSITION となっています。DX11互換対策らしい。

「ふ~ん」と思ってこう書いてください。

 

変数名は何でも良いです。appdata の 変数名と合わせる必要もありません。

 

 vert

ここが Vertexシェーダー です。頂点情報をもとに ゴニョゴニョ します。

 


v2f vert (appdata v) {

v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);

return o;

}

 

変数・関数名をデフォルトと同じにしている限り、赤ハイライトの部分は定型です。

その間に、必要な情報の計算を入れます。

 

今回の例では頂点の座標位置を決定しているのみです。


o.vertex = UnityObjectToClipPos(v.vertex);

この一文も定型だと思って、そのまま記述してください。

これを説明するには、行列変換が云々とかなり難しい話になってしまいます。

そんなことはエンジンの開発者に任せて、我々はおまじないだと思って、ただコピペすれば良いのです。

 

ちなみに、やたらと vertex の文字が出てきて混乱していませんか?

デフォルトでは appdata でも v2f でも vertex という変数名なので区別にしくいですね。

敢えて変数名を変えて書くとこんな感じです。


struct appdata {

float4 input : POSITION;

};

 

struct v2f {

float4 output : SV_POSITION;

};

 

v2f vert (appdata v) {

v2f o;

o.output = UnityObjectToClipPos(v.input);

return o;

}

 

appdataモデルの頂点座標はここだよ。(input

② v2fOK。頂点座標が必要なんだね。じゃぁ、vert君。計算結果をここに入れてね。(output

③ vertなるほど。・・・ほら、画面上の位置が計算できたぜ。(inputoutput

 

 frag

最後に、Vertexシェーダーで計算された情報を利用して、

該当ピクセルを塗りつぶす役割の Fragment シェーダーが働きます。

 

Vertex シェーダーによって、画面上の頂点位置が特定されたので、

そのエリア内を塗りつぶします。


fixed4 frag (v2f i) : SV_Target {

return _Color;

}

 

変数・関数名をデフォルトと同じにしている限り、赤ハイライトの部分は定型です。

そのまま記述してください。

 

そして、Surface Shader とは違い、単純に RGBA を return します

今回の例では、Propaties で設定した _Color で、単純に塗りつぶします。

 

 おわりに

最軽量な V/F Shader を見てきましたが、なんとなくの流れはつかめましたか。

ここに、テクスチャ/UVや頂点カラーなどを足していって、より複雑なピクセル描画を作っていきます。

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)