前書き
DirectX11のリソースバインディングと三角ポリゴン生成についてフローを理解していること前提の記事です
また、この記事の補足記事でもあります
https://qiita.com/YKVKDX12/items/5a2cc3d175b9f6324422
- Assimpの実装概要
Assimp(Open Asset Import Library)を使用して、FBXやOBJなど複数の3Dモデルフォーマットをインポート
m_importer.ReadFileを呼び出し
→aiProcess_ConvertToLeftHandedなどのポストプロセッシングフラグで、頂点の三角化とUV座標の反転
入力レイアウト(POSITION、NORMAL、TEXCOORD)を定義して頂点フォーマットを定義
パイプラインステートはDirectXTKのCommonStatesを使用
これはラスタライザステートを始めとした、サンプラーステート、ブレンドステート
デプスステンシルステートをパパっと作成できるラッパーとなる
使い方は以下の通り・・・
std::unique_ptr<CommonStates> states;
states = std::make_unique<CommonStates>(device);
今回はCullClockwiseでパイプライン側のラスタライザステージに
deviceContext->RSSetState(states->CullCounterClockwise());
とバインドする
2. XTKのリソース生成の概要
DirectX::CreateStaticBuffer で頂点バッファとインデックスバッファを生成
定数バッファはDirectX::ConstantBufferで管理
CreateとSetDataで管理できる
SceneCB?
スクリーンへの座標変換をシェーダーに送り込むための構造体インスタンスとなる
ワールド行列、ビュー行列、プロジェクション行列を格納して頂点シェーダーへ送り込む
なお、これらを乗算した後は逆行列を投影してやらないといけない
その説明やパイプラインの説明をした記事がこちらになる
ところで・・・なんで逆行列を乗算するの????
モデル空間にあった3DCGと関係するベクトルをスクリーン空間に座標変換して持ってきた際、もう一度ローカル空間やワールド空間、ビュー空間といった『ライティングや当たり判定などの計算がしやすい空間』に引き戻すためになる
リソースバインディング
Draw関数内で全リソースをパイプラインにバインド。IASetInputLayoutで入力レイアウトを設定し、IASetIndexBufferでインデックスバッファ、IASetVertexBuffersで頂点バッファをバインドしてから、IASetPrimitiveTopologyでD3D11_PRIMITIVE_TOPOLOGY_TRIANGLELISTを指定
void education::Model::Draw(const DX::DeviceResources* DR) {
if (vertices.empty() || indices.empty()) {
OutputDebugStringA("Vertex or index buffer is empty.\n");
return;
}
auto context = DR->GetD3DDeviceContext();
// Input Layout 設定
context->IASetInputLayout(m_modelInputLayout.Get());
// インデックスバッファの設定
auto indexBuffer = static_cast<ID3D11Buffer*>(m_indexBuffer.Get());
context->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R16_UINT, 0);
// 頂点バッファの設定
UINT stride = sizeof(DirectX::VertexPositionNormalColorTexture);
UINT offset = 0;
auto vertexBuffer = static_cast<ID3D11Buffer*>(m_vertexBuffer.Get());
context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
// プリミティブトポロジー設定
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
auto buffer = m_constantBuffer.GetBuffer();
context->VSSetConstantBuffers(0, 1, &buffer);
context->PSSetConstantBuffers(0, 1, &buffer);
// シェーダー設定
context->VSSetShader(m_vertexShader.Get(), nullptr, 0);
context->PSSetShader(m_pixelShader.Get(), nullptr, 0);
context->PSSetSamplers(0, 1, samplerState.GetAddressOf());
context->PSSetShaderResources(0, 1, m_modelsrv.GetAddressOf());
// 描画コール
context->DrawIndexed(static_cast<UINT>(indices.size()), 0, 0);
}
VSSetConstantBuffersとPSSetConstantBuffers
定数バッファをシェーダースロットに割り当て VSSetShaderとPSSetShaderでコンパイル済みシェーダーコードをパイプラインにセット
最後にPSSetShaderResourcesでテクスチャビューをサンプラーステート経由でバインド
DrawIndexedで描画コマンドを発行
ドローコールの注意点
DrawIndexedではあくまでGPUにこれまでのパイプラインにて行われる一連の処理
をやってほしいと頼むだけである
つまり、ECサイトで言えば「注文確定ボタンを押した」フェイズ
もっとインタラクティブな例で例えるなら
鍵盤楽器(ピアノ)の鍵盤を弾いた
という事に過ぎないのである。
この命令で即座にCGが出力される。というわけではない
(え?ピアノは鍵盤を弾いたら音が出るって?厳密にはそうじゃないんですよ・・・
ピアノの音が出る仕組み知りたくなったでしょ?知的好奇心満たす意味でヤマハさんのこの記事見ましょう)
https://www.yamaha.com/ja/musical_instrument_guide/piano/
(ピアノって今なお現代の最新技術を投入して作られている繊細な工業品なんですよ・・・・)
あと手前味噌ですがこちらも
https://qiita.com/YKVKDX12/items/bbe6e5f358444a92b22c
コード