そろそろD3D9とおさらばしたいということでD3D11を練習し始めました。
4年前にD3D9使い浦島太郎のD3D11入門でも同じことを言われております。
登場以来D3D11のAPIは変わっていないのだけど、最近の更新でSDKがVisualStudio(2012以降?)に付属するようになって、D3DX11が消滅、DirectXMath(旧XNAMath)が導入されるなどの変化があったので、今の開発環境(VisualStudio2013CE)ではどうするのかということをまとめていきたい。
Hello Triangle
まずは、第2話 浦島太郎 魚をつるをベースに三角形を描画してみた。
初期化に関しては、第1話 戻ってきた浦島太郎で説明されているので省略。
DirectX SDKがVisualStudioに含まれるようになったので、個別にSDKをインストールしたり追加のInclude, Libディレクトリを指定する必要がなくなった。新しいD3D11はD3DXが消滅している。あと、XNAMathからDirectXMathになった。
描画関数は以下のようにしたのだけど
void D3D11Manager::Render()
{
////////////////////
// レンダーターゲットのセットと単色クリア
////////////////////
if(!m_renderTarget->IsInitialized()){
// バックバッファの取得
ResPtr<ID3D11Texture2D> pBackBuffer;
m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
reinterpret_cast<void**>(&pBackBuffer));
if(!m_renderTarget->Initialize(m_pDevice, pBackBuffer)){
return;
}
}
m_renderTarget->SetAndClear(m_pDeviceContext);
////////////////////
// 三角形描画
////////////////////
// shader
m_shader->Setup(m_pDeviceContext);
// 描画
// vertex buffer(Input-Assembler stage)
m_IASource->Draw(m_pDeviceContext);
////////////////////
// 描画結果の転送
////////////////////
// render targetへの描画
m_pDeviceContext->Flush();
// 描画済みのrender targetをモニタに出力
m_pSwapChain->Present(NULL, NULL);
}
三角形を描画しているのは次の2行。
// shader
m_shader->Setup(m_pDeviceContext);
// vertex buffer(Input-Assembler stage)
m_IASource->Draw(m_pDeviceContext);
中身は以下のようになっております。
// Shaderのセットアップ
void Setup(ID3D11DeviceContext *pDeviceContext)
{
pDeviceContext->VSSetShader(m_pVsh, NULL, 0);
pDeviceContext->PSSetShader(m_pPsh, NULL, 0);
// 頂点レイアウトのセット
pDeviceContext->IASetInputLayout(m_pInputLayout);
}
void Draw(ID3D11DeviceContext *pDeviceContext)
{
// 頂点バッファ
ID3D11Buffer* pBufferTbl[] = { m_pVertexBuf };
UINT SizeTbl[] = { sizeof(Vertex) };
UINT OffsetTbl[] = { 0 };
pDeviceContext->IASetVertexBuffers(0, 1, pBufferTbl, SizeTbl, OffsetTbl);
// インデックスバッファ
pDeviceContext->IASetIndexBuffer(m_pIndexBuf, DXGI_FORMAT_R32_UINT, 0);
// プリミティブタイプのセット
pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
// Draw
pDeviceContext->DrawIndexed(3 // index count
, 0, 0);
}
ほぼID3D11DeviceContextの呼び出しで、
- ID3D11DeviceContextのVertex-Shader stageにID3D11VertexShaderをセット
- ID3D11DeviceContextにPixel-Shader stageにID3D11PixelShaderをセット
- ID3D11DeviceContextのInput-Assembler stageにID3D11InputLayoutをセット
- ID3D11DeviceContextのInput-Assembler stageのVertexBufferにID3D11Bufferをセット
- ID3D11DeviceContextのInput-Assembler stageにIndexBufferにID3D11Bufferをセット
- ID3D11DeviceContextのInput-Assembler stageにプリミティブ型をセット
- ID3D11DeviceContextのDrawIndexedをコールする
シェーダーをセットしInput-Assemblerに頂点を供給してからDrawをコールするというのが大枠になる。
HLSL記述とInputLayoutの対応について
今回のシェーダーは以下の記述をしていた。
struct VS_INPUT
{
float4 Position : POSITION;
float4 Color : COLOR;
};
struct VS_OUTPUT
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
};
VS_OUTPUT vsMain( VS_INPUT In )
{
VS_OUTPUT Output;
Output.Position = In.Position;
Output.Color = In.Color;
return Output;
}
float4 psMain( VS_OUTPUT In ) : SV_TARGET
{
return In.Color;
}
VertexShaderのエントリポイントが
struct VS_INPUT
{
float4 Position : POSITION;
float4 Color : COLOR;
};
VS_OUTPUT vsMain( VS_INPUT In )
{
// 省略
}
となっている(compileする時に"vsMain"を指定する。hlsl上ではただの関数)。
これは、下記のように書くのとだいたい同じらしく(In変数は無くなる)
VS_OUTPUT vsMain(float4 Color: COLOR, float4 Position: POSITION) // VS_INPUTを順番に展開
{
// 省略
}
VertexShaderステージのvsMainにはfloat4の変数が2つ入力するとわかる。
こいつに対して頂点用のID3D11Bufferを作る時のバイト列を投入したい。
typedef struct D3D11_SUBRESOURCE_DATA {
const void *pSysMem; // <- これ
UINT SysMemPitch;
UINT SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;
プログラム側でバイト列のレイアウトを宣言するのが次のコード。
// Create InputLayout
D3D11_INPUT_ELEMENT_DESC vbElement[] =
{
{
"POSITION" // SemanticName
, 0
, DXGI_FORMAT_R32G32B32A32_FLOAT
, 0
, 0
, D3D11_INPUT_PER_VERTEX_DATA
, 0
},
{
"COLOR" // SemanticName
, 0
, DXGI_FORMAT_R32G32B32A32_FLOAT
, 0
, D3D11_APPEND_ALIGNED_ELEMENT
, D3D11_INPUT_PER_VERTEX_DATA
, 0
}
};
hr = pDevice->CreateInputLayout(vbElement
, sizeof(vbElement) / sizeof(D3D11_INPUT_ELEMENT_DESC)
, vblob->GetBufferPointer(), vblob->GetBufferSize()
, &m_pInputLayout);
D3D11_INPUT_ELEMENT_DESC structure
1頂点につきfloat4に相当するDXGI_FORMAT_R32G32B32A32_FLOATが2つで、それぞれに"POSITION", "COLOR"というSemanticNameがついているという宣言。
コンパイル済みのVertexShaderのバイトコード(vblob)からSemanticsNameを探したりしているのではないか。
"SemanticName"は勝手に決めていいらしくPOSITIONをHOGE, COLORをFUGAに変えてやってみたら問題なく動いた。システム定義のSemanticNameは、プレフィックスに"SV"がつくのでそれに名前が被らなければたぶんOK。
ということで今回はここまで。
次回、ConstantBufferを導入して三角形が回転する。予定。