グラフィックスパイプライン
今回からグラフィックスパイプラインについて見ていきます。 グラフィックスパイプラインはラスタライズ法で描画を行うために設計されたもので、点や線分、三角形を画面に描画する工程となります。 グラフィックパイプラインの全体の流れはドキュメントを参考にしてください。
ドキュメント: グラフィック パイプライン(日本語) Graphics Pipeline(英語)
グラフィックスパイプラインのいくつかの工程にはこちらが用意したシェーダを実行することができます。
それらのシェーダを実装する際は設定する工程に適した処理内容にすることが重要です。
また、エントリポイントに渡される値や出力する値にはセマンティクスと呼ばれる値を識別する用の名前を付ける必要があります。
特に予め用意されているシステムセマンティクスというものがどういった意味を持つのかを理解することはグラフィックスパイプラインを理解することにつながります。
もちろんこちらで自由なセマンティクスをつけることも可能です。
ドキュメント:
セマンティクス(日本語)
Semantics(英語)
また、グラフィックパイプラインを理解する際はデータの流れを意識する必要がとても重要です。 シェーダを実装する際はどのような値がエントリポイントに渡され、どういった値を出力すればいいのかがわかれば、グラフィックスパイプラインを自由に制御することが出来るでしょう。
概要
今パートではグラフィックスパイプラインの基本的なものになる頂点シェーダとピクセルシェーダの2つを使い、画面に点や線分、三角形を描画していきます。 対応しているプロジェクトはPart05_GraphicsPipelineになります。
- 頂点バッファ
- 頂点バッファ
- 入力レイアウト
- GPUへの設定
- 頂点シェーダとピクセルシェーダ
- 頂点シェーダ
- ピクセルシェーダ
- プリミティブトポロジ
- まとめ
- 補足
- システムセマンティクス
1.頂点バッファ
頂点バッファ
グラフィックパイプラインを使って画面に描画する際、頂点バッファと呼ばれるデータを入力値として設定する必要があります。 頂点バッファはID3D11Bufferとして扱い、生成する際はBindFlagsにD3D11_BIND_VERTEX_BUFFERを設定する必要があり、ビューは必要ありません。 その以外は他のバッファと同じです。
入力レイアウト
頂点バッファの内容は自由に決めることが出来ますが、入力レイアウトと呼ばれるもので1要素当たりのデータの並びを指定する必要があります。
入力レイアウトはID3D11InputLayoutと表し、ID3D11InputLayout::CreateInputLayout関数で作成します。
説明が前後しますが、作成するときは入力レイアウトに合う頂点シェーダのコンパイル済みバイナリを渡す必要があります。
ドキュメント:ID3D11Device::CreateInputLayout
(日本語)
(英語)
データの並びはD3D11_INPUT_ELEMENT_DESCの配列を使って指定します。
ドキュメント:D3D11_INPUT_ELEMENT_DESC
(日本語)
(英語)
D3D11_INPUT_ELEMENT_DESCのメンバ
- SemanticName
セマンティクス名
- SemanticIndex
セマンティクス名の番号。同じ名前のものがあった時に識別するためのものになります。
- Format
要素のフォーマット。どのようなフォーマットを設定するかは下のコードを参考にしてください。
- InputSlot
入力スロットの指定。 頂点バッファはGPUに複数設定することができ、この要素がどのスロットのものかを指定するときに使用します。
- AlignedByteOffset
要素内でのオフセット。 D3D11_APPEND_ALIGNED_ELEMENTを指定すると直前の要素の後ろに来るような値が設定されます。
- InputSlotClass
入力データの種類を設定。あとのパートで説明するインスタンス描画のときに使用します。
- InstanceDataStepRate
あとのパートで説明するインスタンス描画のときに使用しますので、省略します。
ちなみに入力レイアウトは使用する頂点シェーダや頂点バッファの個数分生成する必要はなく、同じデータの並びだったり、互換性があるものなら1つの入力レイアウトを使い回すことが可能です。
以上でグラフィックスパイプラインを使う際に必要となる頂点バッファと入力レイアウトについての説明は終わります。
あとはこの2つをグラフィックスパイプラインの始めのステージとなる入力アセンブラステージに設定すれば使うことができます。
ドキュメント:
入力アセンブラー ステージ(日本語)
Input-Assembler Stage(英語)
2.GPUへの設定
次に頂点バッファと入力レイアウトをGPUへ設定する方法とそれを使ってグラフィックスパイプラインを実行する方法について見ていきましょう。
上のID3D11DeviceContext::IASetVertexBuffers関数で頂点バッファを設定しています。
引数について詳しく見て行きませんが、設定するときは頂点バッファの配列以外に1要素のサイズと開始オフセットも一緒に指定する必要があります。
ドキュメント:ID3D11DeviceContext::IASetVertexBuffers
(日本語)
(英語)
続いて入力レイアウトはID3D11DeviceContext::IASetInputLayout関数で設定します。
頂点バッファと入力レイアウトの設定の仕方は以上です。 あとはID3D11DeviceContext::Draw関数でグラフィックスパイプラインを実行すれば、設定した頂点バッファと入力レイアウトが使われます。
ID3D11DeviceContext::Draw関数はドローコール(英訳:Draw Call)と呼ばれ、これ以外にも幾つかの種類があります。 それらについては別パートで詳しく見ていきます。
あと説明が前後しますが、はじめのコードでは頂点バッファと入力レイアウト以外にプリミティブトポロジと頂点シェーダ、ピクセルシェーダの設定も行っています。 それらの説明は後で行いますが、グラフィックスパイプラインを実行する際はこれらも設定する必要があることを覚えておいてください。
ID3D11DeviceContextの関数の名前
これまでID3D11DeviceContextを使ってGPUにいろいろなものを設定してきましたが、その関数には設定する対象に応じて一定のルールがあります。 例えばコンピュートシェーダに関連するものは名前の先頭にCSがついており、今回出てきました入力アセンブラステージの場合はIAがつきます。 他にも頂点シェーダではVSが、ピクセルシェーダにはPSがつきます。 今後グラフィックスパイプラインを見ていくうえで様々なステージが出てきますが、どういったものが設定可能なのかはこのルールを知っていればすぐに分かるようになっています。
3.頂点シェーダとピクセルシェーダ
それでは頂点シェーダとピクセルシェーダについて見ていきましょう。
頂点シェーダ
頂点シェーダは入力アセンブラステージに設定された頂点バッファを入力値として取るシェーダになります。
上のコードが頂点シェーダのとても簡単な実装になります。 行っていることは入力レイアウトで”POSITION”というセマンティクスを持つ頂点バッファの要素を”SV_POSITION”というセマンティクスに変えて次のステージに渡しているだけです。
main関数が呼ばれる回数はDraw Callで指定した頂点数で決まります。 このため頂点シェーダで行う処理は1つの頂点を対象にしたものが適切となるでしょう。 例えば3Dから2Dへの変換行列をかけたり、スキンメッシュアニメーションを適応したりなどなどです。
上のコードでは定数バッファを使用しています。シェーダモデル5.0ならテクスチャも使用可能ですが、それ以前だと使えないシェーダモデルもありますので注意してください。
ちなみにCPU上での頂点シェーダはID3D11VertexShaderで表されます。 生成はID3D11Device::CreateVertexShader関数で行い、コンピュートシェーダと同じように作ります。 コンパイルの際は必ずシェーダモデルに頂点シェーダのものを指定することを忘れないでください。 また頂点シェーダのコンパイル済みバイナリは入力レイアウトの生成時に使用しますので、注意してください。
ピクセルシェーダ
ピクセルシェーダはラスタライザーステージで処理されたデータを受け取り、画面に表示するピクセルの値を計算して返すシェーダになります。
上のピクセルシェーダでは白色のピクセルを画面に出力するものになります。
ピクセルシェーダでは画面の好きな場所に値を出力することはできません。 これはピクセルシェーダの1つ前のステージであるラスタライザーステージで処理するピクセルを決定しているためです。 そのためピクセルシェーダではピクセルの値を決めるための処理を行うのが適切となるでしょう。 SV_POSITIONセマンティクスを指定した値で画面上の位置がわかるようになっていますのでそのような情報が欲しい場合は活用してください。
ラスタライザーステージは頂点シェーダとピクセルシェーダの間で実行されるステージになります。 詳しくは後のパートで説明しますが、頂点シェーダで出力されたデータはこのステージで補間されたり、SV_POSITIONを指定されたものはそれのw成分で割られ、ピクセルシェーダに渡されます。
main関数が呼ばれる回数は頂点バッファと後述するプリミティブトポロジなどで変わってきます。 これは描画する物によって描画範囲が異なるためです。 例えば画面全体を覆う三角形と中央付近に小さく出る三角形では描画されるピクセルの個数は前者が多くなることは明らかです。 グラフィックスパイプラインでは頂点バッファの内容やピクセルシェーダ以前のステージの処理結果などによって自動的に処理を行うピクセル決めてくれます。 もちろんコンピュートシェーダでもこれと同じ機能を実装することはできるでしょうが、GPU自体がこの処理に最適化されているためそのようなことをする意味はないと言ってもいいでしょう。
CPU上ではピクセルシェーダはID3D11PixelShaderと表されます。 頂点シェーダと似たようにID3D11Device::CreatePixelShader関数を使って生成されます。 また、コンパイルの際はシェーダモデルにピクセルシェーダのものを指定することを忘れないで下さい。
頂点シェーダとピクセルシェーダについては以上になります。
4.プリミティブトポロジ
前2つで頂点バッファとシェーダについて見てきました。
ここで見ていくプリミティブトポロジは頂点バッファの各要素のペアを指定するものと言えます。
ドキュメント:
D3D11_PRIMITIVE_TOPOLOGY(日本語)
D3D_PRIMITIVE_TOPOLOGY(英語)
サンプルで使用しているプリミティブトポロジは以下のものになります。
- D3D11_PRIMITIVE_TOPOLOGY_POINTLIST
頂点バッファの内容を点情報として扱います。
- D3D11_PRIMITIVE_TOPOLOGY_LINELIST
頂点バッファの内容を線分情報として扱います。 0と1番目の要素を線分のペアとし、2と3番目、4と5番目...も同じく線分のペアとしてみなします。
- D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
頂点バッファの内容を三角形情報として扱います。 0,1,2番目を三角形の各頂点として、後の3,4,5番目、6,7,8番目...も同じく三角形の頂点としてみなします。
それぞれのプリミティブを指定した時の違いは実際にサンプルを動かして確認してください。 指定できる要素のペアは点と線分と三角形しかありませんので注意してください。
まとめ
今回のパートではグラフィックスパイプラインについて見てきました。 ここで触った頂点バッファ、入力レイアウト、頂点シェーダ、ピクセルシェーダ、プリミティブトポロジがグラフィックスパイプラインの基本要素となりますので覚えておいてください。
補足
システムセマンティクス
頂点シェーダとピクセルシェーダでSV_POSITIONというセマンティクスを使用しました。 SV_POSITIONの前にあるSV_はシステムセマンティクスを表し、グラフィックスパイプラインを実行する上で重要な意味を持ちます。 頂点シェーダでSV_POSITIONを指定した値はラスタライザーステージで加工されピクセル位置に変換されます。
GPUの設定よって上のコードで書いた場所に出るわけではありませんが、意味合いは同じです。 ちなみにSV_POSITIONで指定した値は必ずfloat4型を指定し、w成分は0にならないようにしてください。 これはラスタライザーステージでwの値で割るためです。 なぜ、自動でwの値で割るかと言われれば、変換行列について調べればわかるでしょう。
その他のシステムセマンティクスについてはドキュメントを参照してください。
シェーダステージよっては必須となるものもありますので覚えておいてください。
ドキュメント
セマンティクス(日本語)
Semantics(英語)
<前 | トップ | 次> |