ホーム < ゲームつくろー! < プログラマブルシェーダ編 < 頂点シェーダプログラムの基礎


その3 頂点シェーダプログラムの基礎

 その2で、頂点シェーダプログラムの大きな流れを説明してきました。この章では、実際の頂点シェーダプログラムについて学んでいくことにしましょう。とは言っても、それ程難しいことはありません。むしろ、背景にあるGPUの姿を捉えることの方が大切かもしれません。



@ GPUが頂点を処理する流れ

 頂点シェーダプログラムを組む上で、はずすことが出来ないのがGPUの頂点処理機構です。これは、しっかりと知っておく必要があります。そのために、下の図をご覧下さい。


 大きな図ですいません(詰めましたが限界です(T_T))。この図は頂点シェーダのプログラムがGPUでどう処理されるかを表しています。見た目複雑なのですが、実は大した事ありません。

 まず、頂点データが外部よりやってきます。これには、座標情報があったり、カラー情報やテクスチャ情報が含まれていたりします。これらの情報は「頂点入力レジスタ」というGPU内のメモリに格納されます。入力レジスタは「v○」で表されます。○には0〜15の数字が入ります。つまり、頂点入力レジスタは16個あるわけです。どのレジスタに何の情報が入るかは「頂点シェーダプログラム」が決めます。ですから、v0が必ず頂点であるというわけではありません

 一方SetVertexShaderConstantF関数によって定数データが「定数レジスタ」に格納されます。定数レジスタは256個あります。行列などの情報はここに格納され、変換行列として使われるわけです。定数レジスタはc○と表されます。

 その他にSetVertexShaderConstantB関数や、SetVertexShaderConstantI関数などで分岐データ、反復データがそれぞれ「分岐レジスタ(b○)」「反復レジスタ(i○)」に格納されます。これらはその名の通り分岐命令や反復子として使用されます。一時的に値を保持しておきたい場合などは「一時レジスタ」に値を格納します。このレジスタは唯一読み書きが自由に出来ます。上に挙げた他のレジスタは読み込み専用です。

 頂点入力レジスタに格納された頂点情報を、定数レジスタ、分岐レジスタ、反復レジスタそして一時レジスタ(他にもちょっとあります)の値を元に様々な演算によって変換させ、最終的に「頂点出力レジスタ」に変換後の頂点情報を出力します。このレジスタには名前が付いていますので以下に対応表を示します。

レジスタ 名前 意味
oPos 位置座標 変換後の頂点位置(x,y,z,w) 浮動小数点
oD0 ディフューズ ディフューズカラー(RGBA) 浮動小数点
oD1 スペキュラ スペキュラカラー(RGBA) 浮動小数点
oFog フォグ フォグの値(あまり使われません) 浮動小数点
oPts ポイントサイズ 点ポリゴンの大きさ 浮動小数点
oT# テクスチャ座標(0〜7) テクスチャ座標(4Dベクトル) 浮動小数点


 このGPUの機能と流れをしっかり踏まえて、頂点シェーダプログラムへ進みましょう!大丈夫、簡単なんです(^-^)



A 頂点シェーダプログラム

 頂点シェーダプログラムは昔アセンブリ言語で記述していました。ただ、DirectX9になってからHLSL(High Level Shader Language:上位レベルシェーダ言語)というシェーダ専用のプログラム言語が搭載されるようになりました。これはC言語風の記述でシェーダプログラムが書けてしまう素晴らしい言語です。今後このHLSLがシェーダプログラムの中心になることは間違いありません。しかし、シェーダの深い部分まで知り尽くすには、やはりアセンブリ言語で一度書いてみるべきだろうと思います。そこで、ここではあえてアセンブリに落としてシェーダプログラムを作成することにします。

 作成にあたって、細かな約束事が色々あるのですが、とりあえず例で説明した方が早そうです。シェーダプログラムの中身の一例はこんな感じなんです。

頂点シェーダプログラムの一例
vs_1_1                            // バージョン
dcl_position   v0                 // 位置座標をv0に設定
dcl_color      v1                 // ディフューズカラーをv1に設定
m4x4           oPos,  v0,  c0     // 変換行列によって頂点を変換
mul            oD0 ,  v1,  c4     // ディフーズカラーに定数を掛け算


 このプログラムを読み解きながら頂点シェーダプログラムに慣れていくことにしましょう。
 頂点シェーダプログラムの先頭には、必ず「バージョン番号」を付記します。バージョン番号はその1でも示しましたが、この記事を執筆時(2006年6月)頂点シェーダ3.0が公開されています。ただ、まだ対応ビデオカードが乏しいので、しばらくは1.1か2.0を使うと良いと思います。バージョンは、

v_s_2_0

のようにアンダーバーを加えて書きます。

 次に頂点入力レジスタ番号と頂点情報を対応付けるための宣言をします。これは、2行目および3行目にある「dcl_*****」という宣言で行います。例えばdcl_positionとすると、「頂点座標を使いますよ」という宣言になります。その後ろに空白を空けて、頂点入力レジスタ番号を示します。上の例だとv0になっていますが、これはユーザが好きな番号を使うことができます。v5でもv14でも頂点入力レジスタの範囲内なら何番でもOKです。3行目はディフーズカラーを使う時の宣言です。この宣言文についてまとめたのが以下の表です。

属性 宣言
頂点位置 dcl_position
頂点ブレンドの重み dcl_blendweight
頂点ブレンディングインデックス dcl_blendIndices
頂点法線 dcl_normal
ポイントサイズ dcl_psize
テクスチャ座標 dcl_texcoord
頂点の接線 dcl_tangent
頂点の従法線 dcl_binormal
テッセレーション係数 dcl_tessfactor
座標変換済み頂点位置 dcl_positiont
ディフューズ/スペキュラカラー dcl_color
フォグ dcl_fog
頂点深度 dcl_depth
サンプラデータ dcl_sample


 やけに難しい言葉が並んでいますが、通常は頂点位置、テクスチャ座標、頂点の接線、ディフューズ/スペキュラカラーぐらいしか使いません。これらの詳しい意味はマニュアルのD3DDECLUSAGE 列挙型をご覧下さい。

 宣言が終わったら、具体的に頂点情報を変換するプログラム部分に入ります。上の例ではまず「m4x4」という文字列が来ています。これは「4×4行列の掛け算をしなさい」という命令です。次のoPosは計算結果を格納するレジスタで、ここでは頂点位置であることがわかります。次のv0そしてc0が実際に掛け算される行列(ベクトル)です。これは具体的に言うと次のような行列計算を意味します。

 右辺第1項が変換前の頂点位置座標です。それに行列を掛けて変換後の頂点位置oPosを求める計算になります。右辺第2項の4×4行列に注目です。なぜかc0以外にc1〜c3までが入っていますよね。m4x4命令は連続した4つ分の定数レジスタをいっぺんに参照してくれるんです。また、その並びに注目してください。c#というのが縦に並んでいますね。Direct3Dの行列の場合、これは「横」になります。その2で「行列の転置が必要です」と言ったのは、実はこのせいだったんです。

 その2では、SetVertexShaderConstantF関数を用いてc0〜c3に変換行列の要素を全て格納しました。そうすることで、上のm4x4は「変換行列による頂点」を計算していることになるわけです。ここに、Direct3Dのプログラムと頂点シェーダのプログラムが深く繋がっていることがわかると思います。

 さて、5行目です。この行には「mul」という命令があります。これは「掛け算しなさい」という命令です。掛け算の結果はoD0に入れるようにしています。oD0はディフューズカラーの出力レジスタですから、これは頂点の色を決める計算をしていることになります。掛けるのはv1c4です。v1には事前の宣言から変換前のディフューズカラーが入っており、それに定数c4を掛け算します。c4はやはりSetVertexShaderConstantF関数で値が決められているはずです。
 ディフューズカラーやスペキュラカラーは、Direct3DではDWORDとして定義されますが、頂点シェーダの計算では浮動小数点の計算にスイッチされます。これは、

R G B A
Direct3D 0 〜 255 0 〜 255 0 〜 255 0 〜 255
頂点シェーダ 0.0f〜1.0f〜 0.0f〜1.0f〜 0.0f〜1.0f〜 0.0f〜1.0f〜

という変換になります。頂点シェーダ内での計算で1.0fを超えてしまった場合はすべて255として丸められます。またマイナス値担った時にはすべて0に丸められます。飽和を自動的に処理してくれるのはありがたいものです。ただ、入力カラーのRGBAのどれかが0だった場合、それにいくら掛け算をしても0ですから、これによって必ず色が白くなるというわけでもありません。色を白くさせたいのであれば足し算(add)などを使うべきでしょうね。

 頂点シェーダプログラムの基本的な部分はこれでもうおしまいです。後は、創意工夫で色々な事が出来るのです。ただし、一つ大切な約束があります。頂点宣言で色々な属性を使ったり使わなかったりできるわけですが、必ず頂点位置座標oPosは計算して出力させる必要があります。これが無いとエラーになってしまうので、気を付けて下さい。



B やりたいことを実現するために

 頂点シェーダを使うと、自分が思い浮かべている座標変換や頂点カラーの変更などを行うことが出来ます。頂点シェーダにはそれをサポートする多くの命令群が提供されています。非常に多いため、ここでそれらについて細かく説明することはとてもできません。それら命令群についてはマニュアルの「頂点シェーダ」を参照されると良いでしょう。非常に明確に簡潔に説明があります。

 具体的にどのようなことが出来るかを示してくれるのは、やはり書籍です。世の中には頂点シェーダを使ったエフェクトが沢山あります。それを説明してくれている書籍を調べて、どんどん技術を吸収するのが、シェーダを使いこなす近道です。そのうち、独自のアレンジをしたくなります。そこに表現のオリジナリティが生まれます。ゲームつくろ〜プログラマブルシェーダ編でも、目新しい方法があり次第お伝えしていく予定です。