Win10 ViewModel

DirectX 12 アプリ(ユニバーサル Windows)に ViewModel を組み込みます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プロジェクトの作成

  1. DirectX 12 アプリは、見かけは DirectX 11 アプリと変わりませんが、プログラム的には相当な違いがあるようです。
    特にモデルの生成とモデルの描画に違いがあるようなので ViewModel Class としてまとめてみました。
    DirectX 12 アプリ(ユニバーサル Windows)のサンプルプロジェクトの Sample3DSceneRenderer からモデルの処理を独立します。
  2. Win10 Template を参照して DirectX 12 アプリ(ユニバーサル Windows)を作成して下さい。
    モデルの処理を独立させた Sample3DSceneRenderer.h です。
    "X12ViewModel.h" を取り込んで X12ViewModel *ViewModel; を定義します。
    同様のプログラムが Win10 XLoader に掲載されているので参考にして下さい。
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "ShaderStructures.h"
    #include "..\Common\StepTimer.h"
    #include "X12ViewModel.h"
    
    namespace App1
    {
        // このサンプル レンダリングでは、基本的なレンダリング パイプラインをインスタンス化します。
        class Sample3DSceneRenderer
        {
        public:
            Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources);
            void CreateDeviceDependentResources();
            void CreateWindowSizeDependentResources();
            void Update(DX::StepTimer const& timer);
            bool Render();
            void SaveState();
    
            void StartTracking();
            void TrackingUpdate(float positionX);
            void StopTracking();
            bool IsTracking() { return m_tracking; }
    
        private:
            void LoadState();
            void Rotate(float radians);
    
        private:
            // デバイス リソースへのキャッシュされたポインター。
            std::shared_ptr<DX::DeviceResources> m_deviceResources;
            ModelViewProjectionConstantBuffer       m_constantBufferData;
            X12ViewModel    *ViewModel;
    
            // レンダリング ループで使用する変数。
            float   m_radiansPerSecond;
            float   m_angle;
            bool    m_tracking;
        };
    }
    
  3. モデルの処理を独立させた Sample3DSceneRenderer.cpp です。
    Constructor より CreateDeviceDependentResources() が先に呼ばれるようなので、ここで X12ViewModel を生成してモデルをロードします。
    Render() 関数でモデルを描画するのですが ViewModel->View(); を呼ぶだけです。
    #include "pch.h"
    #include "Sample3DSceneRenderer.h"
    
    #include "..\Common\DirectXHelper.h"
    #include <ppltasks.h>
    #include <synchapi.h>
    
    using namespace App1;
    
    using namespace Concurrency;
    using namespace DirectX;
    using namespace Microsoft::WRL;
    using namespace Windows::Foundation;
    using namespace Windows::Storage;
    
    //インデックスからアプリケーション状態マップへ。
    Platform::String^ AngleKey = "Angle";
    Platform::String^ TrackingKey = "Tracking";
    
    // ファイルから頂点とピクセル シェーダーを読み込み、キューブのジオメトリをインスタンス化します。
    Sample3DSceneRenderer::Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_radiansPerSecond(XM_PIDIV4),  //1 秒間に 45 度の速度で回転させます
        m_angle(0),
        m_tracking(false),
        m_deviceResources(deviceResources)
    {
        LoadState();
        ZeroMemory(&m_constantBufferData, sizeof(m_constantBufferData));
    
        CreateDeviceDependentResources();
        CreateWindowSizeDependentResources();
    }
    
    void Sample3DSceneRenderer::CreateDeviceDependentResources()
    {
        ViewModel = new X12ViewModel(m_deviceResources);
        ViewModel->Load();
    }
    
    // ウィンドウのサイズが変更されたときに、ビューのパラメーターを初期化します。
    void Sample3DSceneRenderer::CreateWindowSizeDependentResources()
    {
        Size outputSize = m_deviceResources->GetOutputSize();
        float aspectRatio = outputSize.Width / outputSize.Height;
        float fovAngleY = 70.0f * XM_PI / 180.0f;
    
        D3D12_VIEWPORT viewport = m_deviceResources->GetScreenViewport();
        ViewModel->m_scissorRect = { 0, 0, static_cast<LONG>(viewport.Width), static_cast<LONG>(viewport.Height)};
    
        // これは、アプリケーションが縦向きビューまたはスナップ ビュー内にあるときに行うことのできる
        // 変更の簡単な例です。
        if (aspectRatio < 1.0f)
        {
            fovAngleY *= 2.0f;
        }
    
        // OrientationTransform3D マトリックスは、シーンの方向を表示方向と
        // 正しく一致させるため、ここで事後乗算されます。
        // この事後乗算ステップは、スワップ チェーンのターゲット ビットマップに対して行われるすべての
        //描画呼び出しで実行する必要があります。他のターゲットに対する呼び出しでは、
        // 適用する必要はありません。
    
        // このサンプルでは、行優先のマトリックスを使用した右辺座標系を使用しています。
        XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
            fovAngleY,
            aspectRatio,
            0.01f,
            100.0f
            );
    
        XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
        XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);
    
        XMStoreFloat4x4(
            &m_constantBufferData.projection,
            XMMatrixTranspose(perspectiveMatrix * orientationMatrix)
            );
    
        // 視点は (0,0.7,1.5) の位置にあり、y 軸に沿って上方向のポイント (0,-0.1,0) を見ています。
        static const XMVECTORF32 eye = { 0.0f, 0.7f, 1.5f, 0.0f };
        static const XMVECTORF32 at = { 0.0f, -0.1f, 0.0f, 0.0f };
        static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
    
        XMStoreFloat4x4(&m_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
    }
    
    // フレームごとに 1 回呼び出し、キューブを回転させてから、モデルおよびビューのマトリックスを計算します。
    void Sample3DSceneRenderer::Update(DX::StepTimer const& timer)
    {
        if (ViewModel->m_loadingComplete)
        {
            if (!m_tracking)
            {
                //キューブを少しだけ回転させます。
                m_angle += static_cast<float>(timer.GetElapsedSeconds()) * m_radiansPerSecond;
    
                Rotate(m_angle);
            }
    
            ViewModel->UpResource(m_constantBufferData);
        }
    }
    
    //レンダラーの現在の状態を保存します。
    void Sample3DSceneRenderer::SaveState()
    {
        auto state = ApplicationData::Current->LocalSettings->Values;
    
        if (state->HasKey(AngleKey))
        {
            state->Remove(AngleKey);
        }
        if (state->HasKey(TrackingKey))
        {
            state->Remove(TrackingKey);
        }
    
        state->Insert(AngleKey, PropertyValue::CreateSingle(m_angle));
        state->Insert(TrackingKey, PropertyValue::CreateBoolean(m_tracking));
    }
    
    //レンダラーを直前の状態に復元します。
    void Sample3DSceneRenderer::LoadState()
    {
        auto state = ApplicationData::Current->LocalSettings->Values;
        if (state->HasKey(AngleKey))
        {
            m_angle = safe_cast<IPropertyValue^>(state->Lookup(AngleKey))->GetSingle();
            state->Remove(AngleKey);
        }
        if (state->HasKey(TrackingKey))
        {
            m_tracking = safe_cast<IPropertyValue^>(state->Lookup(TrackingKey))->GetBoolean();
            state->Remove(TrackingKey);
        }
    }
    
    //3D キューブ モデルを、ラジアン単位で設定された大きさだけ回転させます。
    void Sample3DSceneRenderer::Rotate(float radians)
    {
        // 更新されたモデル マトリックスをシェーダーに渡す準備をします。
        XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
    }
    
    void Sample3DSceneRenderer::StartTracking()
    {
        m_tracking = true;
    }
    
    // 追跡時に、出力画面の幅方向を基準としてポインターの位置を追跡することにより、3D キューブを Y 軸に沿って回転させることができます。
    void Sample3DSceneRenderer::TrackingUpdate(float positionX)
    {
        if (m_tracking)
        {
            float radians = XM_2PI * 2.0f * positionX / m_deviceResources->GetOutputSize().Width;
            Rotate(radians);
        }
    }
    
    void Sample3DSceneRenderer::StopTracking()
    {
        m_tracking = false;
    }
    
    // 頂点とピクセル シェーダーを使用して、1 つのフレームを描画します。
    bool Sample3DSceneRenderer::Render()
    {
        return ViewModel->View();
    }
    
  4. モデルの生成とモデルの描画をまとめた ViewModel Class のヘッダーファイル X12ViewModel.h です。
    XLoader に比べて随分領域が多く、その分処理が面倒になっています。
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "ShaderStructures.h"
    #include "..\Common\StepTimer.h"
    
    namespace App1
    {
        // このサンプル レンダリングでは、基本的なレンダリング パイプラインをインスタンス化します。
        class X12ViewModel
        {
        public:
            X12ViewModel(const std::shared_ptr<DX::DeviceResources>& deviceResources);
            ~X12ViewModel();
            void Load();
            void CreateModel();
            bool View();
            void UpResource(ModelViewProjectionConstantBuffer BufferData);
            D3D12_RECT  m_scissorRect;
            bool        m_loadingComplete;
    
        private:
            // 定数バッファーは 256 バイトで整列していなければなりません。
            static const UINT c_alignedConstantBufferSize = (sizeof(ModelViewProjectionConstantBuffer) + 255) & ~255;
    
            // デバイス リソースへのキャッシュされたポインター。
            std::shared_ptr<DX::DeviceResources> m_deviceResources;
    
            // キューブ ジオメトリの Direct3D リソース。
            Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList>   m_commandList;
            Microsoft::WRL::ComPtr<ID3D12RootSignature>         m_rootSignature;
            Microsoft::WRL::ComPtr<ID3D12PipelineState>         m_pipelineState;
            Microsoft::WRL::ComPtr<ID3D12DescriptorHeap>        m_cbvHeap;
            Microsoft::WRL::ComPtr<ID3D12Resource>              m_vertexBuffer;
            Microsoft::WRL::ComPtr<ID3D12Resource>              m_indexBuffer;
            Microsoft::WRL::ComPtr<ID3D12Resource>              m_constantBuffer;
            UINT8*                                              m_mappedConstantBuffer;
            UINT                                                m_cbvDescriptorSize;
            std::vector<byte>                                   m_vertexShader;
            std::vector<byte>                                   m_pixelShader;
            D3D12_VERTEX_BUFFER_VIEW                            m_vertexBufferView;
            D3D12_INDEX_BUFFER_VIEW                             m_indexBufferView;
        };
    }
    
  5. ViewModel Class のプログラムファイル X12ViewModel.cpp です。
    こちらも XLoader に比べて処理が面倒になっています。
    Model を作成する処理を CreateModel() 関数として独立させてみました。
    #include "pch.h"
    #include "X12ViewModel.h"
    
    #include "..\Common\DirectXHelper.h"
    #include <ppltasks.h>
    #include <synchapi.h>
    
    using namespace App1;
    using namespace Concurrency;
    using namespace DirectX;
    using namespace Microsoft::WRL;
    using namespace Windows::Foundation;
    using namespace Windows::Storage;
    
    // ファイルから頂点とピクセル シェーダーを読み込み、キューブのジオメトリをインスタンス化します。
    X12ViewModel::X12ViewModel(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_loadingComplete(false),
        m_mappedConstantBuffer(nullptr),
        m_deviceResources(deviceResources)
    {
    }
    
    X12ViewModel::~X12ViewModel()
    {
        m_constantBuffer->Unmap(0, nullptr);
        m_mappedConstantBuffer = nullptr;
    }
    
    void X12ViewModel::Load()
    {
    OutputDebugString(L"**Start Load\n");
        auto d3dDevice = m_deviceResources->GetD3DDevice();
    
        //単一の定数バッファー スロットがあるルート署名を作成します。
        {
            CD3DX12_DESCRIPTOR_RANGE range;
            CD3DX12_ROOT_PARAMETER parameter;
    
            range.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
            parameter.InitAsDescriptorTable(1, &range, D3D12_SHADER_VISIBILITY_VERTEX);
    
            D3D12_ROOT_SIGNATURE_FLAGS rootSignatureFlags =
                D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | //定数バッファーにアクセスする必要があるのは、入力アセンブラー ステージだけです。
                D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
                D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
                D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
                D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
    
            CD3DX12_ROOT_SIGNATURE_DESC descRootSignature;
            descRootSignature.Init(1, ¶meter, 0, nullptr, rootSignatureFlags);
    
            ComPtr<ID3DBlob> pSignature;
            ComPtr<ID3DBlob> pError;
            DX::ThrowIfFailed(D3D12SerializeRootSignature(&descRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, pSignature.GetAddressOf(), pError.GetAddressOf()));
            DX::ThrowIfFailed(d3dDevice->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
            NAME_D3D12_OBJECT(m_rootSignature);
        }
    
        // シェーダーを非同期で読み込みます。
        auto createVSTask = DX::ReadDataAsync(L"SampleVertexShader.cso").then([this](std::vector<byte>& fileData) {
            m_vertexShader = fileData;
        });
    
        auto createPSTask = DX::ReadDataAsync(L"SamplePixelShader.cso").then([this](std::vector<byte>& fileData) {
            m_pixelShader = fileData;
        });
    
        //シェーダーが読み込まれた後に、パイプライン状態を作成します。
        auto createPipelineStateTask = (createPSTask && createVSTask).then([this]() {
    
            static const D3D12_INPUT_ELEMENT_DESC inputLayout[] =
            {
                { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
                { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            };
    
            D3D12_GRAPHICS_PIPELINE_STATE_DESC state = {};
            state.InputLayout = { inputLayout, _countof(inputLayout) };
            state.pRootSignature = m_rootSignature.Get();
            state.VS = CD3DX12_SHADER_BYTECODE(&m_vertexShader[0], m_vertexShader.size());
            state.PS = CD3DX12_SHADER_BYTECODE(&m_pixelShader[0], m_pixelShader.size());
            state.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
            state.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
            state.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
            state.SampleMask = UINT_MAX;
            state.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
            state.NumRenderTargets = 1;
            state.RTVFormats[0] = m_deviceResources->GetBackBufferFormat();
            state.DSVFormat = m_deviceResources->GetDepthBufferFormat();
            state.SampleDesc.Count = 1;
    
            DX::ThrowIfFailed(m_deviceResources->GetD3DDevice()->CreateGraphicsPipelineState(&state, IID_PPV_ARGS(&m_pipelineState)));
    
            // シェーダー データは、パイプライン状態が作成された後に削除できます。
            m_vertexShader.clear();
            m_pixelShader.clear();
        });
        createPipelineStateTask.then([this]()
        {   CreateModel();
        });
    }
    
    // Model を作成
    void X12ViewModel::CreateModel()
    {
        //キューブ ジオメトリ リソースを作成して GPU にアップロードします。
        auto d3dDevice = m_deviceResources->GetD3DDevice();
    
        // コマンド一覧を作成します。
        DX::ThrowIfFailed(d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_deviceResources->GetCommandAllocator(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList)));
        NAME_D3D12_OBJECT(m_commandList);
    
        //キューブの頂点。各頂点には位置と色があります。
        VertexPositionColor cubeVertices[] =
        {
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f) },
            { XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) },
            { XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) },
            { XMFLOAT3(-0.5f,  0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f) },
            { XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) },
            { XMFLOAT3(0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f) },
            { XMFLOAT3(0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f) },
            { XMFLOAT3(0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f) },
        };
    
        const UINT vertexBufferSize = sizeof(cubeVertices);
    
        //GPU の既定のヒープに頂点バッファー リソースを作成し、アップロード ヒープを使用して、頂点データをそれにコピーします。
        //アップロード リソースは、GPU による使用が終了するまで解放できません。
        Microsoft::WRL::ComPtr<ID3D12Resource> vertexBufferUpload;
    
        CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT);
        CD3DX12_RESOURCE_DESC vertexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
        DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
            &defaultHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &vertexBufferDesc,
            D3D12_RESOURCE_STATE_COPY_DEST,
            nullptr,
            IID_PPV_ARGS(&m_vertexBuffer)));
    
        CD3DX12_HEAP_PROPERTIES uploadHeapProperties(D3D12_HEAP_TYPE_UPLOAD);
        DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
            &uploadHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &vertexBufferDesc,
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&vertexBufferUpload)));
    
        NAME_D3D12_OBJECT(m_vertexBuffer);
    
        //頂上バッファーを GPU にアップロードします。
        {
            D3D12_SUBRESOURCE_DATA vertexData = {};
            vertexData.pData = reinterpret_cast<BYTE*>(cubeVertices);
            vertexData.RowPitch = vertexBufferSize;
            vertexData.SlicePitch = vertexData.RowPitch;
    
            UpdateSubresources(m_commandList.Get(), m_vertexBuffer.Get(), vertexBufferUpload.Get(), 0, 0, 1, &vertexData);
    
            CD3DX12_RESOURCE_BARRIER vertexBufferResourceBarrier =
                CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
            m_commandList->ResourceBarrier(1, &vertexBufferResourceBarrier);
        }
    
        //メッシュ インデックスを読み込みます。インデックスの各 3 つ組みは、画面に表示する三角形を表します。
        //たとえば、0,2,1 は、頂点バッファーにあるインデックス 0、2、および 1 の各頂点によって、このメッシュの
        // 最初の三角形を構成することを意味します。
        unsigned short cubeIndices[] =
        {
            0, 2, 1, 1, 2, 3,   // -x
            4, 5, 6, 5, 7, 6,   // +x
            0, 1, 5, 0, 5, 4,   // -y
            2, 6, 7, 2, 7, 3,   // +y
            0, 4, 6, 0, 6, 2,   // -z
            1, 3, 7, 1, 7, 5,   // +z
        };
    
        const UINT indexBufferSize = sizeof(cubeIndices);
    
        //GPU の既定のヒープにインデックス バッファー リソースを作成し、アップロード ヒープを使用してインデックス データをそれにコピーします。
        //アップロード リソースは、GPU による使用が終了するまで解放できません。
        Microsoft::WRL::ComPtr<ID3D12Resource> indexBufferUpload;
    
        CD3DX12_RESOURCE_DESC indexBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(indexBufferSize);
        DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
            &defaultHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &indexBufferDesc,
            D3D12_RESOURCE_STATE_COPY_DEST,
            nullptr,
            IID_PPV_ARGS(&m_indexBuffer)));
    
        DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
            &uploadHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &indexBufferDesc,
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&indexBufferUpload)));
    
        NAME_D3D12_OBJECT(m_indexBuffer);
    
        //インデックス バッファーを GPU にアップロードします。
        {
            D3D12_SUBRESOURCE_DATA indexData = {};
            indexData.pData = reinterpret_cast<BYTE*>(cubeIndices);
            indexData.RowPitch = indexBufferSize;
            indexData.SlicePitch = indexData.RowPitch;
    
            UpdateSubresources(m_commandList.Get(), m_indexBuffer.Get(), indexBufferUpload.Get(), 0, 0, 1, &indexData);
    
            CD3DX12_RESOURCE_BARRIER indexBufferResourceBarrier =
                CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER);
            m_commandList->ResourceBarrier(1, &indexBufferResourceBarrier);
        }
    
        // 定数バッファーのために記述子ヒープを作成します。
        {
            D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
            heapDesc.NumDescriptors = DX::c_frameCount;
            heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
            //このフラグは、この記述子ヒープをパイプラインに結合できること、およびそれに含まれる記述子をルート テーブルから参照できることを示します。
            heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
            DX::ThrowIfFailed(d3dDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&m_cbvHeap)));
    
            NAME_D3D12_OBJECT(m_cbvHeap);
        }
    
        CD3DX12_RESOURCE_DESC constantBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(DX::c_frameCount * c_alignedConstantBufferSize);
        DX::ThrowIfFailed(d3dDevice->CreateCommittedResource(
            &uploadHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &constantBufferDesc,
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&m_constantBuffer)));
    
        NAME_D3D12_OBJECT(m_constantBuffer);
    
        // アップロード バッファーにアクセスするための定数バッファー ビューを作成します。
        D3D12_GPU_VIRTUAL_ADDRESS cbvGpuAddress = m_constantBuffer->GetGPUVirtualAddress();
        CD3DX12_CPU_DESCRIPTOR_HANDLE cbvCpuHandle(m_cbvHeap->GetCPUDescriptorHandleForHeapStart());
        m_cbvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    
        for (int n = 0; n < DX::c_frameCount; n++)
        {
            D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
            desc.BufferLocation = cbvGpuAddress;
            desc.SizeInBytes = c_alignedConstantBufferSize;
            d3dDevice->CreateConstantBufferView(&desc, cbvCpuHandle);
    
            cbvGpuAddress += desc.SizeInBytes;
            cbvCpuHandle.Offset(m_cbvDescriptorSize);
        }
    
        // 定数バッファーをマッピングします。
        CD3DX12_RANGE readRange(0, 0);      // CPU 上のこのリソースから読み取りません。
        DX::ThrowIfFailed(m_constantBuffer->Map(0, &readRange, reinterpret_cast<void**>(&m_mappedConstantBuffer)));
        ZeroMemory(m_mappedConstantBuffer, DX::c_frameCount * c_alignedConstantBufferSize);
        //アプリが終了するまで、このマッピングを解除しません。リソースの存続期間中、マッピングを維持しても問題ありません。
    
        // コマンド リストを閉じて、それを実行することにより、GPU の既定のヒープへの頂点/インデックス バッファーのコピーを開始します。
        DX::ThrowIfFailed(m_commandList->Close());
        ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
        m_deviceResources->GetCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
    
        //頂上/インデックス バッファー ビューを作成します。
        m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
        m_vertexBufferView.StrideInBytes = sizeof(VertexPositionColor);
        m_vertexBufferView.SizeInBytes = sizeof(cubeVertices);
    
        m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress();
        m_indexBufferView.SizeInBytes = sizeof(cubeIndices);
        m_indexBufferView.Format = DXGI_FORMAT_R16_UINT;
    
        //コマンド リストが実行を完了するまで待機します。頂点/インデックス バッファーは、アップロード リソースが範囲外になる前にアップロードする必要があります。
        m_deviceResources->WaitForGpu();
        m_loadingComplete = true;
    }
    
    // 頂点とピクセル シェーダーを使用して、1 つのフレームを描画します。
    bool X12ViewModel::View()
    {
        // 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
        if (!m_loadingComplete)
        {   return false;  }
    
        DX::ThrowIfFailed(m_deviceResources->GetCommandAllocator()->Reset());
    
        // ExecuteCommandList() が呼び出された後、コマンド一覧をいつでもリセットできます。
        DX::ThrowIfFailed(m_commandList->Reset(m_deviceResources->GetCommandAllocator(), m_pipelineState.Get()));
    
        PIXBeginEvent(m_commandList.Get(), 0, L"Draw the cube");
        {
            //このフレームで使用されるグラフィックのルート署名と記述子ヒープを設定します。
            m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
            ID3D12DescriptorHeap* ppHeaps[] = { m_cbvHeap.Get() };
            m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
    
            // 現在のフレームの定数バッファーをパイプラインにバインドします。
            CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle(m_cbvHeap->GetGPUDescriptorHandleForHeapStart(), m_deviceResources->GetCurrentFrameIndex(), m_cbvDescriptorSize);
            m_commandList->SetGraphicsRootDescriptorTable(0, gpuHandle);
    
            // ビューポートとシザリング四角形を設定します。
            D3D12_VIEWPORT viewport = m_deviceResources->GetScreenViewport();
            m_commandList->RSSetViewports(1, &viewport);
            m_commandList->RSSetScissorRects(1, &m_scissorRect);
    
            //このリソースをレンダー ターゲットとして使用することを指定します。
            CD3DX12_RESOURCE_BARRIER renderTargetResourceBarrier =
                CD3DX12_RESOURCE_BARRIER::Transition(m_deviceResources->GetRenderTarget(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
            m_commandList->ResourceBarrier(1, &renderTargetResourceBarrier);
    
            //描画コマンドを記録します。
            D3D12_CPU_DESCRIPTOR_HANDLE renderTargetView = m_deviceResources->GetRenderTargetView();
            D3D12_CPU_DESCRIPTOR_HANDLE depthStencilView = m_deviceResources->GetDepthStencilView();
            m_commandList->ClearRenderTargetView(renderTargetView, DirectX::Colors::CornflowerBlue, 0, nullptr);
            m_commandList->ClearDepthStencilView(depthStencilView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
    
            m_commandList->OMSetRenderTargets(1, &renderTargetView, false, &depthStencilView);
    
            m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
            m_commandList->IASetIndexBuffer(&m_indexBufferView);
            m_commandList->DrawIndexedInstanced(36, 1, 0, 0, 0);
    
            //レンダー ターゲットが、コマンド リストがいつ実行を完了するかを表すために使用されることを示します。
            CD3DX12_RESOURCE_BARRIER presentResourceBarrier =
                CD3DX12_RESOURCE_BARRIER::Transition(m_deviceResources->GetRenderTarget(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
            m_commandList->ResourceBarrier(1, &presentResourceBarrier);
        }
        PIXEndEvent(m_commandList.Get());
    
        DX::ThrowIfFailed(m_commandList->Close());
    
        //コマンド リストを実行します。
        ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
        m_deviceResources->GetCommandQueue()->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
    
        return true;
    }
    
    void X12ViewModel::UpResource(ModelViewProjectionConstantBuffer BufferData)
    {
        UINT8* destination = m_mappedConstantBuffer + (m_deviceResources->GetCurrentFrameIndex() * c_alignedConstantBufferSize);
        memcpy(destination, &BufferData, sizeof(BufferData));
    }
    
  6. コンパイル&実行するとカラーキューブが回転しながら描画されます。
    システムで面倒を見ていた処理が、ユーザーレベルに降りてきたようでプログラムが面倒になっていますが、その分きめの細かい操作ができそうです。

超初心者のプログラム入門(DirectX Store)