Top / D3D9使い浦島太郎のD3D11入門 / 第2話 浦島太郎 魚をつる

第2話 浦島太郎 魚をつる

とりあえず初期化が終わったので次はポリゴン描画でしょうか。
特に深い意味でつけたタイトルではないので突っ込まないでください・・。
ポリゴンが・・・いやなんでもないです(汗

ポリゴン描画


サンプルのダウンロード

バッファの作成

さて、まずはバッファの作成からです。
D3D9ではVertexBufferとかIndexBufferとか作成してましたが、
D3D11ではあらゆるバッファは共通化され、ID3D11Bufferポインタを利用します。

VertexBufferの作成

ではVertexBufferの作成から見ていきます。

bool createVB()
{
    HRESULT hr;

    // Create VB
    VertexArray pVertices[] = 
    {
	    { Vector4( 0.0f,  0.0f, 0.0f, 1.0f), Vector4( 1.0f, 0.0f, 0.0f, 1.0f)},
	    { Vector4( 0.5f,  0.5f, 0.0f, 1.0f), Vector4( 0.0f, 1.0f, 0.0f, 1.0f)},
	    { Vector4( 0.5f, -0.5f, 0.0f, 1.0f), Vector4( 0.0f, 0.0f, 1.0f, 1.0f)}
    };
    unsigned int vsize = sizeof(pVertices);

    D3D11_BUFFER_DESC vdesc;
    ZeroMemory(&vdesc, sizeof(vdesc));
    vdesc.ByteWidth      = vsize;
    vdesc.Usage          = D3D11_USAGE_DEFAULT;
    vdesc.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
    vdesc.CPUAccessFlags = 0;

    D3D11_SUBRESOURCE_DATA vertexData;
    ZeroMemory(&vertexData, sizeof(vertexData));
    vertexData.pSysMem = pVertices;

    hr = pDevice->CreateBuffer(&vdesc, &vertexData, &pVertexBuf);
    if (FAILED(hr))
	    return false;

    return true;
}

基本的な内容はCreateBuffer()関数を呼べばいいだけです。ただD3D9で今までCreateVertexBuffer()
として引数に直接VertexBufferの大きさを指定していたところが、わざわざ構造体を利用して引数を設定
しないといけないのがちょっと面倒なところですね。
わかってしまえばVBもIBも同じなので迷うことはなさそうです。

IndexBufferの作成

次はIndexBufferの作成です。VBと同じ方法で作成できます。
BindFlags = D3D11_BIND_INDEX_BUFFERと変えるだけです。
しかし、あえて別の、mapする方法で作成します。
先のVBの方法で作成したバッファの内容はあとからCPUで変更することができません。
後からバッファの内容を変更する可能性がある場合は以下の様にバッファ作成を行います。

bool createIB()
{
    HRESULT hr;

    // Create IB
    unsigned int pIndices[] = 
    {
	    0,1,2
    };
    unsigned int isize = sizeof(pIndices);

    D3D11_BUFFER_DESC idesc;
    ZeroMemory(&idesc, sizeof(idesc));
    idesc.ByteWidth      = isize;
    idesc.Usage          = D3D11_USAGE_DYNAMIC;
    idesc.BindFlags      = D3D11_BIND_INDEX_BUFFER;
    idesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    hr = pDevice->CreateBuffer(&idesc, NULL, &pIndexBuf);
    if (FAILED(hr))
	    return false;

    D3D11_MAPPED_SUBRESOURCE ires;
    hr = pDeviceContext->Map(pIndexBuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &ires);
    if (FAILED(hr))
	    return false;

    void* idxbuf = ires.pData;
    if (idxbuf)
    {
	    memcpy(idxbuf, pIndices, isize);
	    pDeviceContext->Unmap(pIndexBuf, 0);
    }

    return true;
}

D3D9時代もLock()/Unlock() のセットで利用したかと思います。D3D11では Map()/Unmap() に変わった
だけですね。基本的に結果は変わりませんが、VBでやったようにD3D11はリソース作成時に直接バッファ
内容を書き込むことができるようになっているので、そちらを使うことをお勧めいたします。
そんな手間でもないですしね。

シェーダの作成

そしてシェーダの作成です。D3D11は何か描画するためには必ずシェーダを作成する必要があります。
もう古き良き固定パイプラインはないんですねー。シェーダの読み込みはD3DX関数を利用すれば
一発です。適当にエラーメッセージを表示してくれる関数でまとまっています。

HRESULT CompileShaderFromFile( const char* filename, const char* entryPoint,
                                 const char* shaderModel, ID3DBlob** ppBlobOut )
{
    HRESULT hr = S_OK;

    DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

    ID3DBlob* pErrorBlob;
    hr = D3DX11CompileFromFile(filename, NULL, NULL, entryPoint, shaderModel, 
	    dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );

    if( FAILED(hr) )
    {
	    if( pErrorBlob != NULL )
		    OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
    }
    if (pErrorBlob)
    {
	    pErrorBlob->Release();
	    pErrorBlob = NULL;
    }

    return hr;
}

bool createShaders()
{
    HRESULT hr;
    // vertex shader
    ID3DBlob* vblob = NULL;
    hr = CompileShaderFromFile("test.fx", "vsMain", "vs_4_0_level_9_1", &vblob);
    if (FAILED(hr))
	    return false;
    hr = pDevice->CreateVertexShader( vblob->GetBufferPointer(),
				    vblob->GetBufferSize(), NULL, &pVsh);
    if (FAILED(hr))
	    return false;

    // pixel shader
    ID3DBlob* pblob = NULL;
    hr = CompileShaderFromFile("test.fx", "psMain", "ps_4_0_level_9_1", &pblob);
    if (FAILED(hr))
	    return false;
    hr = pDevice->CreatePixelShader( pblob->GetBufferPointer(),
				    pblob->GetBufferSize(), NULL, &pPsh);
    if (FAILED(hr))
	    return false;

    return true;
}

なんだか面倒ですが、ID3DBlob* blob; というバッファのような形式を経由する必要があります。
しかもどうもこのID3DBlobですが、現段階のSDK(Feb2010)ではID3D10Blobのtypedefになってます。
どうやらまだこの部分は更新が行われていない模様。もしかしたらJune2010で変わるかもしれません。
CompileShaderFromFileで指定したblobはコンパイルしたシェーダの埋め込みのデバッグ情報とシンボル
テーブル情報が格納されます。PixelShaderのものはもう使わないので捨ててよいのですが、
VertexShaderのblobだけ後で利用するので注意です。

その他必要なもの

InputLayout

まだ必要なものがあります。D3D9のときVertexDeclarationとか作成しましたよね。
え?知らない?まさかFVF使いですか。そうですか・・。
いや、いいんです。そんなのはどっちでもいいんです。なんてったってD3D11ではどちらもなくなりました。
D3D11ではInputLayoutです。

    // Create InputLayout
    hr = pDevice->CreateInputLayout(vbElement,
        sizeof(vbElement)/sizeof(D3D11_INPUT_ELEMENT_DESC),
        vblob->GetBufferPointer(), vblob->GetBufferSize(), &pInputLayout);
    if (FAILED(hr))
        return false;

実はここでさっきVertexShaderをコンパイルして作成したblobを利用します。InputLayoutはVertexShader
に入力するフォーマットを指定するので必要となるようです。
しかしblobを渡すのはいまいち美しくないと思うのは私だけ?

ラスタライザステート

D3D9ではレンダリングステートとか呼んでいたものの設定。とりあえずカリングやらFILLモードやらの設定
これもわざわざCreateしないといけないのはマルチスレッド対策だろうね。まぁしかたないかね。

    D3D11_RASTERIZER_DESC rsDesc;
    ZeroMemory( &rsDesc, sizeof( D3D11_RASTERIZER_DESC ) );
    rsDesc.CullMode = D3D11_CULL_BACK;
    rsDesc.FillMode = D3D11_FILL_SOLID; 
    rsDesc.DepthClipEnable = TRUE;
    if( FAILED(pDevice->CreateRasterizerState( &rsDesc, &pRS )))
        return false;

デプスステンシルステート

同様にデプスバッファののレンダリングステートまぁここは大体こんなもんか。

    D3D11_DEPTH_STENCIL_DESC dsDesc;
    ZeroMemory( &dsDesc, sizeof( D3D11_DEPTH_STENCIL_DESC ) );
    dsDesc.DepthEnable	= TRUE;
    dsDesc.DepthWriteMask	= D3D11_DEPTH_WRITE_MASK_ALL;
    dsDesc.DepthFunc	= D3D11_COMPARISON_LESS;
    dsDesc.StencilEnable	= FALSE;
    if(FAILED(pDevice->CreateDepthStencilState( &dsDesc, &pDS ) ) )
        return false;

やっと描画

ここまで用意できたら後は描画するだけ。

void Render()
{
    // クリア
    Clear();

    // VBのセット
    ID3D11Buffer* pBufferTbl[] = { pVertexBuf };
    UINT SizeTbl[] = { sizeof(VertexArray) };
    UINT OffsetTbl[] = { 0 };
    pDeviceContext->IASetVertexBuffers(0, 1, pBufferTbl, SizeTbl, OffsetTbl);
    // IBのセット
    pDeviceContext->IASetIndexBuffer(pIndexBuf, DXGI_FORMAT_R32_UINT, 0);

    // ILのセット
    pDeviceContext->IASetInputLayout(pInputLayout);
    // プリミティブタイプのセット
    pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

    // Shaderのセットアップ
    pDeviceContext->VSSetShader(pVsh, NULL, 0);
    pDeviceContext->PSSetShader(pPsh, NULL, 0);

    // Stateの設定
    pDeviceContext->RSSetState( pRS );
    pDeviceContext->OMSetDepthStencilState( pDS, 0 );

    // 描画
    pDeviceContext->DrawIndexed(3, 0, 0);
    pDeviceContext->Flush();

    // バッファスワップ
    SwapBuffer();
}

描画系のコマンドはデバイスではなく、デバイスコンテキストにあるのを思い出してください。
よくみるとpDeviceContext->VS**** とか pDeviceContext->IA**** になっています。
このままではなんのこっちゃわかりませんが、これらはパイプラインの頭文字です。
詳しくは次のリンクを参照してください。D3D11グラフィックスパイプライン(MSDN)

簡単に書いておくとD3D11ではパイプラインの順番に
Input Assembler Stage IA***
Vertex Shader Stage VS***
Hull Shader HS***
Domain Shader Stage DS***
Geometry Shader Stage GS***
Stream Output Stage SO***
Rasterizer Stage RS***
Pixel Shader Stage PS***
Output Merger Stage OM***
それと
ComputeShader CS***

と言うような略語命令なっています。 D3D11では固定パイプラインがないといいつつも、上のリンクの図の
プログラマブルパイプラインのパイプライン順番をよく覚えておく必要がありそうです。

描画の説明にもどります。VBの設定がちょっと面倒なこと以外はむしろD3D9より簡単かもしれません。
VBの設定が面倒なのは複数ストリームの設定をできるからです。D3D9でもストリームは使えましたね。
それなりのD3D9使いであればあとの説明は不要でしょう。

まとめ

いかがだったでしょうか。何かやたら構造体を利用したリソースの作成が多いですね。
いままで引数がだらだらあったのでまとめるようにしたのでしょうか。
しかしバッファ系はまとまって共通化されたのである意味すっきりしましたね。
ひとつパターンを覚えると使い回しが利きそうです。
それにしても描画系コマンドは頭文字略語の嵐です。
もう一度言いますがこの図(MSDN)をよく覚える必要があります。

あ、シェーダの説明をし忘れましたが、大して難しいことをしてないので・・・。
詳しい説明はまた後々の回でやることにします。(できればエフェクトフレームワークのあたり)
実はCompileShaderFromFileのところで、
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;を指定しないと、つまり
DWORD dwShaderFlags = 0;
としておくとD3D9のシェーダファイルとかでもコンパイルして使うことができます。
# てかこの辺のD3D10とか書くところもJune2010で直るのかな・・・?

サンプルのダウンロード