~プログラミング~ DirectX 11で三角形を表示しよう

C++言語でDirectX 11を使ったアプリケーションを開発してみよう。 

 

DirectXの初期化シェーダーの準備が出来たらようやく図形が表示できます。

ここでは、簡単な三角形を表示させてみようと思います。

 

各頂点に色情報を持たせて色付きのポリゴンにしてみます。

 


頂点バッファーを作る

頂点データの準備

三角形を作るので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_DESCD3D11_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メソッドで描画します。

 

 

 

 

 

 

 

うまく出来たらこんな感じに表示されると思います。

 

ソースコードはこちら


コメントをお書きください

コメント: 3
  • #1

    nanashi (土曜日, 21 8月 2021 18:48)

    こんばんは。
    私はブログ主様の記事を参考にシェーダファイルコンパイルが完了し、現在、三角形を描画する処理を書いていて三角形描画の処理を書き終わったので実行してみたのですが、
    ブログ主様のソースコードで言うと、CD3DTestクラスのCreate関数を実行すると、ID3D11DeviceContextがnullptrとなり、エラーになってしまうのです。
    このエラーは、ウインドウ生成などのタイミングで、ID3D11DeviceContextの中身が入っていないから起こるエラーなのでしょうか。
    恐縮ですが、ご教示いただけますと幸いです。

  • #2

    AraramiStudio (月曜日, 23 8月 2021 11:05)

    nanashiさん
    コメントありがとうございます。

    ID3D11DeviceContextのインスタンスはD3D11CreateDeviceAndSwapChainメソッドで生成されます。
    D3D11CreateDeviceAndSwapChainメソッドでエラーが起きていると思われるので、メソッドの戻り値を確認してみると原因が分かるかもしれません。

  • #3

    nanashi (土曜日, 28 8月 2021 22:24)

    AraramiStudio様
    返信ありがとうございます。

    返り値を確認してみると、私が引数に使っていた自作の関数が間違っていたようでした。
    ご教示頂いたおかげで、解決しました。ありがとうございます。