003.四角形の回転と移動(Dx11版)

 このサンプルはSimplSample003というディレクトリに含まれます。
 BaseCrossDx11.slnというソリューションを開くとDx11版が起動します。

 実行結果は以下のような画面が出ます。単純な四角形が回転しながら左右に移動します。

 

図0003a

 


 動画は以下になります。

 

 

【共通解説】

 Dx12、Dx11両方に共通なのはシェーダーです。DxSharedプロジェクト内にシェーダファイルというフィルタがあり、そこに記述されてます。
 今回使用するシェーダは頂点シェーダとピクセルシェーダです。VertexPositionColor型の頂点を持ち、コンスタントバッファからの入力で、位置を変更させています。
 SimpleSample002と同じシェーダです。

【Dx11版解説】

 BaseCrossDx11.slnを開くと、BaseCrossDx11というメインプロジェクトがあります。この中のCharacter.h/cppが主な記述個所になります。
 オブジェクトのクラスはSquareSpriteクラスです。

■初期化■

 初期化はSquareSprite::OnCreate関数から呼ばれます。
 ここではインデックスバッファを作成します。作成方法は、頂点配列インデックス配列を作成して、
    //メッシュの作成
    m_SquareMesh = MeshResource::CreateMeshResource(vertices, indices, false);
 を呼びます。

■更新処理■

 更新処理は回転しながら移動を実装してます。左右の一定以上の場所で、回転方向と移動方向を変えています。
void SquareSprite::OnUpdate() {
    if (m_PosSpan.x > 0) {
        m_Rot -= 0.05f;
    }
    else {
        m_Rot += 0.05f;
    }
    m_Pos += m_PosSpan;
    if (abs(m_Pos.x) > 400.0f) {
        m_PosSpan *= -1.0f;
    }
}
 このほかにもいろいろ記述方法はあると思いますので、各自いろいろやってみると勉強になります。

■描画処理■

 こちらもSimpleSample002とほとんど同じです。違うのはインデックス描画をするところです。あと、コンスタントバッファへ渡すワールド行列の作成が少し違います。
void SquareSprite::OnDraw() {
    //中略

    //行列の定義
    Mat4x4 World, Proj;
    //ワールド行列の決定
    World.affineTransformation2D(
        m_Scale,            //スケーリング
        Vec2(0, 0),     //回転の中心(重心)
        m_Rot,              //回転角度
        m_Pos               //位置
    );
    //射影行列の決定
    float w = static_cast<float>(App::GetApp()->GetGameWidth());
    float h = static_cast<float>(App::GetApp()->GetGameHeight());
    Proj = XMMatrixOrthographicLH(w, h, -1.0, 1.0f);
    //行列の合成
    World *= Proj;

    //コンスタントバッファの準備
    SpriteConstantBuffer sb;
    //エミッシブ加算は行わない。
    sb.Emissive = Col4(0, 0, 0, 0);
    //行列の設定
    sb.World = World;
    //コンスタントバッファの更新
    pD3D11DeviceContext->UpdateSubresource(CBSprite::GetPtr()->GetBuffer(), 0, nullptr, &sb, 0, 0);

    //ストライドとオフセット
    UINT stride = sizeof(VertexPositionColor);
    UINT offset = 0;
    //頂点バッファのセット
    pD3D11DeviceContext->IASetVertexBuffers(0, 1,
         m_SquareMesh->GetVertexBuffer().GetAddressOf(), &stride, &offset);
    //インデックスバッファのセット
    pD3D11DeviceContext->IASetIndexBuffer(m_SquareMesh->GetIndexBuffer().Get(),
         DXGI_FORMAT_R16_UINT, 0);

    //描画方法(3角形)
    pD3D11DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    //コンスタントバッファの設定
    ID3D11Buffer* pConstantBuffer = CBSprite::GetPtr()->GetBuffer();
    ID3D11Buffer* pNullConstantBuffer = nullptr;
    //頂点シェーダに渡す
    pD3D11DeviceContext->VSSetConstantBuffers(0, 1, &pConstantBuffer);
    //ピクセルシェーダに渡す
    pD3D11DeviceContext->PSSetConstantBuffers(0, 1, &pConstantBuffer);
    //シェーダの設定
    pD3D11DeviceContext->VSSetShader(VSPCSprite::GetPtr()->GetShader(), nullptr, 0);
    pD3D11DeviceContext->PSSetShader(PSPCSprite::GetPtr()->GetShader(), nullptr, 0);
    //インプットレイアウトの設定
    pD3D11DeviceContext->IASetInputLayout(VSPCSprite::GetPtr()->GetInputLayout());

    //ブレンドステート
    pD3D11DeviceContext->OMSetBlendState(RenderState->GetOpaque(), nullptr, 0xffffffff);
    //デプスステンシルステート
    pD3D11DeviceContext->OMSetDepthStencilState(RenderState->GetDepthNone(), 0);
    //ラスタライザステート
    pD3D11DeviceContext->RSSetState(RenderState->GetCullBack());

    //描画
    pD3D11DeviceContext->DrawIndexed(m_SquareMesh->GetNumIndicis(), 0, 0);
    //後始末
    Dev->InitializeStates();

}
 このように、ワールド行列Worldを作成するために、World.AffineTransformation2D関数を使用しています。またそれによって作成した行列に、ゲーム画面を基準とした射影行列を掛け算して、最終的なシェーダに渡す行列を作成します。(詳細は上記、Dx12のコンスタントバッファ更新の説明もお読みください)

描画処理後半のインデックス描画の方法は、Dx12版と違いはあまりありません。

【まとめ】

 今回は、前回のSimpleSample002シェーダーや頂点型を変えずにインデックス描画をする方法をサンプル化しました。またスプライトの座標系についても少し触れました。スプライトの座標系は必ずしもこの方法がベストかどうかは議論も残るところですが、いずれにせよ一つのゲームでは一つの座標系で記述したほうが、グラフィッカーとのやり取りを考えてもよろしいかと思います。