DirectX(D3D11.1)再入門 Input-Assembler

そろそろD3D9とおさらばしたいということでD3D11を練習し始めました。

4年前に[D3D9使い浦島太郎のD3D11入門](http://kioku.sys-k.net/d3d11/)でも同じことを言われております。

登場以来D3D11のAPIは変わっていないのだけど、最近の更新でSDKがVisualStudio(2012以降？)に付属するようになって、D3DX11が消滅、DirectXMath(旧XNAMath)が導入されるなどの変化があったので、今の開発環境(VisualStudio2013CE)ではどうするのかということをまとめていきたい。

# Hello Triangle
まずは、[第2話 浦島太郎 魚をつる](http://kioku.sys-k.net/d3d11/story/story02.htm)をベースに三角形を描画してみた。
![MinTriangle.png](https://qiita-image-store.s3.amazonaws.com/0/59664/196d5124-5218-ef1f-82ea-c1eb58cee0e6.png)


[今回のソース](https://github.com/ousttrue/MinTriangle/tree/HelloTriangle)

初期化に関しては、[第１話 戻ってきた浦島太郎](http://kioku.sys-k.net/d3d11/story/story01.htm)で説明されているので省略。

DirectX SDKがVisualStudioに含まれるようになったので、個別にSDKをインストールしたり追加のInclude, Libディレクトリを指定する必要がなくなった。新しいD3D11はD3DXが消滅している。あと、XNAMathからDirectXMathになった。

描画関数は以下のようにしたのだけど

```cpp: 
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);
}
```

三角形を描画しているのは次の２行。

```cpp:
    // shader
    m_shader->Setup(m_pDeviceContext);

    // vertex buffer(Input-Assembler stage)
    m_IASource->Draw(m_pDeviceContext);
```

中身は以下のようになっております。

```cpp:
    // 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](http://msdn.microsoft.com/en-us/library/windows/desktop/ff476385(v=vs.85).aspx)の呼び出しで、

* 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の対応について
今回のシェーダーは以下の記述をしていた。

```hlsl:
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のエントリポイントが

```hlsl:
struct VS_INPUT
{
    float4 Position   : POSITION;
    float4 Color      : COLOR;
};

VS_OUTPUT vsMain( VS_INPUT In )
{
// 省略
}
```

となっている(compileする時に"vsMain"を指定する。hlsl上ではただの関数)。
これは、下記のように書くのとだいたい同じらしく(In変数は無くなる)

```hlsl:
VS_OUTPUT vsMain(float4 Color: COLOR, float4 Position: POSITION) // VS_INPUTを順番に展開
{
// 省略
}
```
VertexShaderステージのvsMainにはfloat4の変数が２つ入力するとわかる。

こいつに対して頂点用のID3D11Bufferを作る時のバイト列を投入したい。

```cpp
typedef struct D3D11_SUBRESOURCE_DATA {
    const void *pSysMem; // <- これ
    UINT SysMemPitch;
    UINT SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;
```    
    
プログラム側でバイト列のレイアウトを宣言するのが次のコード。

```cpp:
        // 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](http://msdn.microsoft.com/en-us/library/windows/desktop/ff476180(v=vs.85).aspx)


１頂点につきfloat4に相当するDXGI_FORMAT_R32G32B32A32_FLOATが２つで、それぞれに"POSITION", "COLOR"というSemanticNameがついているという宣言。
コンパイル済みのVertexShaderのバイトコード(vblob)からSemanticsNameを探したりしているのではないか。


"SemanticName"は勝手に決めていいらしくPOSITIONをHOGE, COLORをFUGAに変えてやってみたら問題なく動いた。システム定義のSemanticNameは、プレフィックスに"SV"がつくのでそれに名前が被らなければたぶんOK。

ということで今回はここまで。

次回、ConstantBufferを導入して三角形が回転する。予定。

# 参考
- [D3D9使い浦島太郎のD3D11入門](http://kioku.sys-k.net/d3d11/)
- [Getting Started with the Input-Assembler Stage](http://msdn.microsoft.com/en-us/library/windows/desktop/bb205117(v=vs.85).aspx)
- [D3DX11CompileFromFileWの代替コード](https://github.com/ousttrue/MinTriangle/blob/HelloTriangle/MinTriangle/CompileShaderFromFile.cpp)