こんにちわ、Unityエンジニアのオオバです。

前回記事「Unity 動的にメッシュを生成してゴニョゴニョする : 超基本編」の続きです。

スクリプトで三角形ポリゴンを動的に生成し、
テクスチャを貼るところまで来ました。

順番が逆かもしれませんが、
三角形に頂点カラーを反映してみます。

動的にメッシュを生成するシリーズの一貫です。
Unity動的にメッシュを生成するシリーズ

この記事の内容

頂点カラーとは

そもそも頂点には以下のように複数のデータを格納することができ、このデータのことを頂点バッファと呼ぶこともあります。

頂点カラーとは、一つの頂点にカラー情報をもたせたカラーの事を言います。

ちなみに今回使用する頂点バッファには以下の型を含みます。

ちなみに頂点を結ぶ順番は頂点バッファではなくインデックスバッファと呼びます。
頂点バッファには含まれません。

では頂点カラーを反映させるコードを書いてみます。前回作成したDynamicCreateMeshクラスを変更していきます。
DynamicCreateMesh3.cs · GitHub

◆26行目辺り

// 変更箇所 : 各頂点に色情報を設定  
mesh.colors = new Color[] {  
  Color.white,  
  Color.red,  
  Color.green  
};  

meshインスタンスのcolorsプロパティに各頂点のカラーを配列でセットします。

Unity 動的に生成したメッシュに対して頂点カラーを反映する_0

頂点カラー未反映の三角形ですが、各頂点に白色、赤色、緑色を設定したイメージです。

頂点カラーを反映するシェーダーを書く

Unityにはデフォルトで頂点カラーを表示させるビルトインシェーダーが無いため書いていきます。

Unity 動的に生成したメッシュに対して頂点カラーを反映する_1

ProjectブラウザのCreateボタンからShader -> Unlit Shaderを選んで作成します。

SimpleVertexColorShader.shader · GitHub

少し長くなりそうですが、このシェーダーの解説をしていきます。

1つのシェーダーファイル(SimpleVertexColor.shader)の中には2種類のシェーダーが内包されています。

この2つのシェーダーの大まかな流れを説明すると、Vertexシェーダーで作られたデータは、Fragmentシェーダーに渡され描画されます。

シェーダーとはなんぞや?という方はこちらの記事をどうぞ
「シェーダー」とは何なのか、を自分の言葉でまとめてみる

Vertexシェーダーとは

CPUから渡ってきた頂点情報を加工するのがVertexシェーダーの仕事です。

v2f vert (appdata v)  
{
 v2f o;  
 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);  
 o.color = v.color;  
 return o;  
}

vert関数が毎フレームこのシェーダーに紐づくメッシュの頂点数ごとに実行されます。今回の場合は、3頂点あるため、毎フレーム3回実行されます。

そもそもvert関数引数のappdataとはなんぞやという話になりますが、これはCPU側から送られる頂点バッファです。

struct appdata  
{
 float4 vertex : POSITION;  
 fixed3 color : COLOR0;  
};  

『型 変数名 : セマンティクス』という文法で構造体で定義されます。
セマンティクス とは、シェーダーの入出力時に使用し、どのような目的を持っているかということを示す文字列です。

POSITIONCOLOR0
頂点座標頂点カラー

※この他にも様々なセマンティクスが存在します。興味ある方はコチラをどうぞ。

結局今回のVertexシェーダーで何をしているのかというと以下の3つです。

三角形メッシュ頂点の座標変換

メッシュがどうやってモニタに表示されているのか、という事を真面目に考えてみます。

  1. ローカル座標をグローバル座標に変換する(モデル座標変換)
  2. カメラを原点としてカメラの移動分の値をメッシュのグローバル座標にオフセットする(ビュー座標変換)
  3. カメラの画角、クリッピングなどの設定から映す範囲を指定する(投影変換)
  4. 描画範囲に合わせた変換(ビューポート変換)
  5. ディプレイの解像度に合わせてピクセルを割り当てる(ラスタライズ)
  6. テクスチャや色を合成
  7. モニタに表示される

このような長い道のりを経てモニタにメッシュが映しだされるわけですが、この道程の中でVertexシェーダーが担う部分は、1〜4(確か)の部分です。

本来ならたくさんの行列計算をしないといけないのですが、1〜4の部分を先の1行で書き終わることが出来ます。Unity恐ろしい....。

o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);  

このo.vertexの中身は全ての座標変換が終わった頂点座標が格納されています。
(この頂点座標の型がfloat3ではなく、float4なのかは別記事で。)

最初に説明したようにVertexシェーダーで作られたデータ(v2f構造体)はFragmentシェーダーに渡されます。

v2fもappdataと同じようなものでVertexシェーダーからFragmentシェーダーに渡されるデータの集合体で、予め何を送るか定義するものです。中身を確認すると、

struct v2f  
{
 float4 vertex : SV_POSITION;  
 fixed3 color : COLOR0;  
};  

Fragmentシェーダーに渡されるものは、頂点座標頂点カラーということが見て取れます。
SV_POSITIONセマンティクスが、POSITIONではないのはDX11への互換性確保とのことです。

参考
Unity のシェーダーの基礎を勉強してみたのでやる気出してまとめてみた - 凹みTips

ちなみにFragmentシェーダーは各頂点情報からどういうふうに描画するかということを担います。Vertexシェーダーから送られるv2fを元に描画処理をしていきます。

Fragmentシェーダーの処理

FragmentシェーダーはVertexシェーダーと違い、頂点数分だけ実行されるわけではありません。1フレーム辺り、頂点が結ぶ描画エリアのピクセル数分frag関数が実行されます。今回の記事ではあまり関係ありませんが、複雑な計算処理はFragmentシェーダーではなくVertexシェーダーで行う方がパフォーマンスは良くなります。

fixed4 frag (v2f i) : SV_Target  
{
 return fixed4(i.color, 1);  
}

フラグメントシェーダーの処理部分です。
頂点カラーを描画するだけなので非常に簡潔です。

v2fのカラー情報を取得して、アウトプットしています。
※フラグメントシェーダーで、v2fのvertex使ってないから省略しても良いのでは?という疑問も湧くのですが、vertexは必須のプロパティなので省略できません。(省略するとエラー)

Unity 動的に生成したメッシュに対して頂点カラーを反映する_2

チュートリアル: 頂点プログラムとフラグメントプログラム - Unity マニュアル

結果色のついた三角形メッシュが描画されます。

Unity 動的に生成したメッシュに対して頂点カラーを反映する_3

各頂点間はシェーダー側で勝手にリニア補間が入るため、頂点間はグラデーションになります。

まとめ

終盤かなり駆け足になりましたが、頂点カラーを描画することが出来ました。
次回は頂点カラー + テクスチャ描画をやってみます。

その他の動的にメッシュを生成するシリーズ

Unity動的にメッシュを生成するシリーズ

期間限定 最大95%オフセール
効率UPメガバンドル開催中!最大95%オフ!!!
期間 : 11月1日午後15時59分まで
オススメ記事
検証環境