009.立方体の描画(Dx11版)

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

 実行結果は以下のような画面が出ます。

 

図0009a

 


 動画は以下になります。

 

 

【サンプルのポイント】

 今項から3Dの描画になります。手始めにVertexPositionColor型の頂点を持つ立方体の描画です。
 まず頂点バッファを自作する処理からやってみます。立法や球体など、基本的な形状はライブラリを使って作成することができます。ですが、今項では、自作する方法の説明から始めます。

【共通解説】

 Dx12、Dx11両方に共通なのはシェーダーです。DxSharedプロジェクト内にシェーダファイルというフィルタがあり、そこに記述されてます。
 今回使用するシェーダは頂点シェーダとピクセルシェーダです。VertexPositionColor型の頂点を持つものです。コンスタントバッファもあります。
 以下は頂点シェーダです。VSPCStatic.hlslです。
#include "INCStructs.hlsli"

cbuffer SimpleConstantBuffer : register(b0)
{
    float4x4 World  : packoffset(c0);
    float4x4 View   : packoffset(c4);
    float4x4 Projection : packoffset(c8);
    float4 Emissive : packoffset(c12);
};

PSPCInput main(VSPCInput input)
{
    PSPCInput result;
    //頂点の位置を変換
    float4 pos = float4(input.position.xyz, 1.0f);
    //ワールド変換
    pos = mul(pos, World);
    //ビュー変換
    pos = mul(pos, View);
    //射影変換
    pos = mul(pos, Projection);
    //ピクセルシェーダに渡す変数に設定
    result.position = pos;
    result.color = input.color;
    return result;
}
 コンスタントバッファを見るとわかりますが、3D描画には3つの行列をシェーダに渡します。C++側で計算してもいいのですが、シェーダで計算したほうが速いのでそのように記述しています。
 頂点シェーダの出力型であるPSPCInputは、INCStructs.hlsliに定義されているわけですが、以下のような構造です。
struct PSPCInput
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};
 このようにポジションカラーのみあります。頂点シェーダからピクセルシェーダに渡すポジションデバイス座標になります。ですから、頂点シェーダでは、
    PSPCInput result;
    //頂点の位置を変換
    float4 pos = float4(input.position.xyz, 1.0f);
    //ワールド変換
    pos = mul(pos, World);
    //ビュー変換
    pos = mul(pos, View);
    //射影変換
    pos = mul(pos, Projection);
 のようにワールド行列、ビュー行列、射影行列を、順に掛け算します。ローカル座標のある頂点に対して、この順番で掛け算すると、デバイス座標上のポジションに変換されます。その際気を付けなければいけないのは、float4型を使わなければいけないという部分です。そして、wの値は1.0にしておきます。

 ピクセルシェーダは色を決定する処理です。各自読んでみましょう。

【Dx11版解説】

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

■初期化■

 Dx11版の初期化は、頂点配列インデックス配列を作成し、それをもとにメッシュを作成します。この処理はDx12版と変わりません。

■更新処理■

 CubeObject::OnUpdate()関数です。ここではオブジェクトを回転させています。

■描画処理■

 このサンプルの描画はDx11描画を、CubeObject::OnDraw()関数に直接記述しています
void CubeObject::OnDraw() {
    auto Dev = App::GetApp()->GetDeviceResources();
    auto pD3D11DeviceContext = Dev->GetD3DDeviceContext();
    auto RenderState = Dev->GetRenderState();

    //行列の定義
    Mat4x4 World, View, Proj;
    //ワールド行列の決定
    World.affineTransformation(
        m_Scale,            //スケーリング
        Vec3(0, 0, 0),      //回転の中心(重心)
        m_Qt,               //回転角度
        m_Pos               //位置
    );
    //転置する
    World.transpose();
    //ビュー行列の決定
    View = XMMatrixLookAtLH(Vec3(0, 2.0, -5.0f), Vec3(0, 0, 0), Vec3(0, 1.0f, 0));
    //転置する
    View.transpose();
    //射影行列の決定
    float w = static_cast<float>(App::GetApp()->GetGameWidth());
    float h = static_cast<float>(App::GetApp()->GetGameHeight());
    Proj = XMMatrixPerspectiveFovLH(XM_PIDIV4, w / h, 1.0f, 100.0f);
    //転置する
    Proj.transpose();
    //コンスタントバッファの準備
    PCStaticConstantBuffer sb;
    sb.World = World;
    sb.View = View;
    sb.Projection = Proj;
    //エミッシブ加算は行わない。
    sb.Emissive = Col4(0, 0, 0, 0);
    //コンスタントバッファの更新
    pD3D11DeviceContext->UpdateSubresource(CBPCStatic::GetPtr()->GetBuffer(), 0, nullptr, &sb, 0, 0);

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

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

    //コンスタントバッファの設定
    ID3D11Buffer* pConstantBuffer = CBPCStatic::GetPtr()->GetBuffer();
    ID3D11Buffer* pNullConstantBuffer = nullptr;
    //頂点シェーダに渡す
    pD3D11DeviceContext->VSSetConstantBuffers(0, 1, &pConstantBuffer);
    //ピクセルシェーダに渡す
    pD3D11DeviceContext->PSSetConstantBuffers(0, 1, &pConstantBuffer);
    //シェーダの設定
    pD3D11DeviceContext->VSSetShader(VSPCStatic::GetPtr()->GetShader(), nullptr, 0);
    pD3D11DeviceContext->PSSetShader(PSPCStatic::GetPtr()->GetShader(), nullptr, 0);
    //インプットレイアウトの設定
    pD3D11DeviceContext->IASetInputLayout(VSPCStatic::GetPtr()->GetInputLayout());
    //ブレンドステート
    pD3D11DeviceContext->OMSetBlendState(RenderState->GetOpaque(), nullptr, 0xffffffff);
    //デプスステンシルステート
    pD3D11DeviceContext->OMSetDepthStencilState(RenderState->GetDepthDefault(), 0);
    //ラスタライザステート
    pD3D11DeviceContext->RSSetState(RenderState->GetCullBack());

    //描画
    pD3D11DeviceContext->DrawIndexed(m_CubeMesh->GetNumIndicis(), 0, 0);
    //後始末
    Dev->InitializeStates();
}
 このようにコンスタントバッファの更新もこの関数内で行ってます。
 注意したいのは3D用の処理です。赤くなってますが、デプスステンシルステートRenderState->GetDepthDefault()を渡します。これは、深度バッファを使う設定です。

【まとめ】

 今項はテクスチャや法線を含みませんので、比較的単純な3Dの描画を行ってます。次項ではもう少し複雑になります。