C++言語でDirectX 11を使ったアプリケーションを開発してみよう。
頂点バッファーを作る
頂点データの準備
三角形を作るので3点の頂点データを作ります。
データはとりあえずグローバル変数として用意してみました。
各頂点は座標 x,y,z と色 r,g,b,a の情報を持てる Vertex という構造体を作ってみました。
struct Vertex { float pos[ 3 ]; float col[ 4 ]; }; Vertex g_VertexList[] { { { -0.5f, 0.5f, 0.5f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, { { 0.5f, -0.5f, 0.5f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, { { -0.5f, -0.5f, 0.5f }, { 0.0f, 0.0f, 1.0f, 1.0f } } };
頂点バッファーの作成
頂点データをDirect3Dのパイプラインに流し込む為のバッファーを作成します。
バッファーの作成にはID3D11Device::CreateBufferメソッドを使います。
D3D11_BUFFER_DESC bufferDesc; bufferDesc.ByteWidth = sizeof( Vertex ) * 3; bufferDesc.Usage = D3D11_USAGE_DEFAULT; bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDesc.CPUAccessFlags = 0; bufferDesc.MiscFlags = 0; bufferDesc.StructureByteStride = 0; D3D11_SUBRESOURCE_DATA subResourceData; subResourceData.pSysMem = g_VertexList; subResourceData.SysMemPitch = 0; subResourceData.SysMemSlicePitch = 0; hr = m_pDevice->CreateBuffer( &bufferDesc, &subResourceData, &m_pVertexBuffer ); if ( FAILED( hr ) ) return hr;
D3D11_BUFFER_DESC と D3D11_SUBRESOURCE_DATA という2つのパラメータを使ってバッファーを作成します。
D3D11_BUFFER_DESC
ByteWidth ・・・ バッファーのサイズを指定します。
BindFlags ・・・ 頂点バッファーとしてバインドする為 D3D11_BIND_VERTEX_BUFFER
を指定します。
D3D11_SUBRESOURCE_DATA
pSysMem ・・・ リソースデータのポインタを指定します。
その他の項目はテクスチャデータの場合にだけ使うものなので「0」を指定します。
インプットレイアウトを作る
インプットレイアウトとは、入力されたバッファーがどのようなデータ構造なのかを記述するものです。
DirectX 9 の時にはIDirect3DDevice9::SetFVFメソッドなどで頂点フォーマットを指定していました。
DirectX11 では入力バッファーがより柔軟なものになっており、データ構造の記述にはID3D11InputLayoutを使うようになっています。
頂点データの構造を記述
D3D11_INPUT_ELEMENT_DESC を使って頂点データの構造を定義します。
今回は座標と色を持つ構造体なのでD3D11_INPUT_ELEMENT_DESCは2レコードです。
それぞれ HLSL セマンティクスや要素のフォーマットなどを定義します。
D3D11_INPUT_ELEMENT_DESC g_VertexDesc[] { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, };
インプットレイアウトの作成
上記の構造定義と頂点シェーダーのコンパイル済みコードを使ってインプットレイアウトを作成します。
シェーダーのコンパイル済みについてはこちらを参照してください。
インプットレイアウトの作成にはID3D11Device::CreateInputLayoutメソッドを使います。
hr = m_pDevice->CreateInputLayout( g_VertexDesc, ARRAYSIZE( g_VertexDesc ), g_vs_main, sizeof( g_vs_main ), &m_pInputLayout ); if ( FAILED( hr ) ) return hr;
シェーダーオブジェクトを作る
コンパイル済みコードのバイト配列から頂点シェーダーとピクセルシェーダーのオブジェクトを作成します。
頂点シェーダーオブジェクトの作成にはID3D11Device::CreateVertexShaderメソッドを
ピクセルシェーダーオブジェクトの作成にはID3D11Device::CreatePixelShaderメソッドを使います。
hr = m_pDevice->CreateVertexShader( &g_vs_main, sizeof( g_vs_main ), NULL, &m_pVertexShader ); if ( FAILED( hr ) ) return hr; hr = m_pDevice->CreatePixelShader( &g_ps_main, sizeof( g_ps_main ), NULL, &m_pPixelShader ); if ( FAILED( hr ) ) return hr;
ビューポートを作る
ビューポートは -1.0~1.0 の範囲で作られたワールド座標をスクリーン座標(表示するウインドウのサイズ)に変換するための情報で D3D11_VIEWPORT を使って定義します。
CRect rect; ::GetClientRect( hwnd, &rect ); m_Viewport.TopLeftX = 0; m_Viewport.TopLeftY = 0; m_Viewport.Width = (FLOAT)rect.Width(); m_Viewport.Height = (FLOAT)rect.Height(); m_Viewport.MinDepth = 0.0f; m_Viewport.MaxDepth = 1.0f;
描画する
必要なオブジェクトが準備出来たら、それらをデバイスコンテキストへセットして描画します。
UINT strides = sizeof( Vertex ); UINT offsets = 0; m_pImmediateContext->IASetInputLayout( m_pInputLayout ); m_pImmediateContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &strides, &offsets ); m_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); m_pImmediateContext->VSSetShader( m_pVertexShader, NULL, 0 ); m_pImmediateContext->RSSetViewports( 1, &m_Viewport ); m_pImmediateContext->PSSetShader( m_pPixelShader, NULL, 0 ); m_pImmediateContext->Draw( 3, 0 ); m_pSwapChain->Present( 0, 0 );
インプットレイアウト → ID3D11DeviceContext::IASetInputLayoutメソッド
頂点バッファ → ID3D11DeviceContext::IASetVertexBuffersメソッド
頂点シェーダー → ID3D11DeviceContext::VSSetShaderメソッド
ピクセルシェーダー → ID3D11DeviceContext::PSSetShaderメソッド
ビューポート → ID3D11DeviceContext::RSSetViewportsメソッド
さらに頂点バッファがどのような順番で三角形を作るかをID3D11DeviceContext::IASetPrimitiveTopologyメソッドで指定します。
すべてセット出来たらID3D11DeviceContext::Drawメソッドで描画します。
- ~プログラミング~ DirectX 11を始めよう
- ~プログラミング~ DirectX 11はDirectX 9と何が違うか その1
- ~プログラミング~ DirectX 11はDirectX 9と何が違うか その2
- ~プログラミング~ DirectX 11の初期化をしよう その1
- ~プログラミング~ DirectX 11の初期化をしよう その2
- ~プログラミング~ DirectX 11で深度/ステンシルバッファを準備しよう
- ~プログラミング~ DirectX 11でシェーダーをコンパイルしよう
- ~プログラミング~ DirectX 11で三角形を表示しよう
- ~プログラミング~ DirectX 11の頂点バッファとインデックスバッファ
- ~プログラミング~ DirectX 11で立体を表示しよう その1
- ~プログラミング~ DirectX 11で立体を表示しよう その2
- ~プログラミング~ DirectX 11で平行光源ライティング
- ~プログラミング~ DirectX 11で点光源ライティング
- ~プログラミング~ DirectX 11で拡散反射 (Diffuse reflection)
- ~プログラミング~ DirectX 11で鏡面反射 (Specular reflection)
- ~プログラミング~ DirectX 11で環境反射 (Ambient reflection)
- ~プログラミング~ DirectX 11でフォン反射モデル
- ~プログラミング~ DirectXTexライブラリを導入しよう
- ~プログラミング~ DirectX 11でテクスチャマッピング
コメントをお書きください
nanashi (土曜日, 21 8月 2021 18:48)
こんばんは。
私はブログ主様の記事を参考にシェーダファイルコンパイルが完了し、現在、三角形を描画する処理を書いていて三角形描画の処理を書き終わったので実行してみたのですが、
ブログ主様のソースコードで言うと、CD3DTestクラスのCreate関数を実行すると、ID3D11DeviceContextがnullptrとなり、エラーになってしまうのです。
このエラーは、ウインドウ生成などのタイミングで、ID3D11DeviceContextの中身が入っていないから起こるエラーなのでしょうか。
恐縮ですが、ご教示いただけますと幸いです。
AraramiStudio (月曜日, 23 8月 2021 11:05)
nanashiさん
コメントありがとうございます。
ID3D11DeviceContextのインスタンスはD3D11CreateDeviceAndSwapChainメソッドで生成されます。
D3D11CreateDeviceAndSwapChainメソッドでエラーが起きていると思われるので、メソッドの戻り値を確認してみると原因が分かるかもしれません。
nanashi (土曜日, 28 8月 2021 22:24)
AraramiStudio様
返信ありがとうございます。
返り値を確認してみると、私が引数に使っていた自作の関数が間違っていたようでした。
ご教示頂いたおかげで、解決しました。ありがとうございます。