Win10 Men4 2Model

Windows10 DirectX3D で4面体のモデルを並べて描画します。

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

プログラムの説明

  1. Windows10 で Class CreateColor を組み込んで、4面体のモデルを並べて描画します。
    Win10 Template を参照して DirectX 11 アプリ(ユニバーサル Windows) を生成して下さい。
    実行するとカラーキューブが回転しながら描画されます。
    次に Class CreateColor を組み込んで、4面体のモデルを描画します。
    4面体も「頂点+カラー」のモデルなのでシェーダー関係はそのまま使います。
    詳細は Win10 Create Color を参照して下さい。
  2. Sample3DSceneRenderer.h では、二体のモデル領域を定義します。
    vertexBuffer, indexBuffer, constantBufferData, indexCount を対で定義しています。
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "ShaderStructures.h"
    #include "..\Common\StepTimer.h"
    #include "CreateColor.h"
    
    namespace App1
    {
        // このサンプル レンダリングでは、基本的なレンダリング パイプラインをインスタンス化します。
        class Sample3DSceneRenderer
        {
        public:
            Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources);
            void CreateDeviceDependentResources();
            void CreateWindowSizeDependentResources();
            void ReleaseDeviceDependentResources();
            void Update(DX::StepTimer const& timer);
            void Render();
            void StartTracking();
            void TrackingUpdate(float positionX);
            void StopTracking();
            bool IsTracking() { return m_tracking; }
    
        private:
            void Rotate(float radians);
    
        private:
            // デバイス リソースへのキャッシュされたポインター。
            std::shared_ptr<DX::DeviceResources> m_deviceResources;
    
            // キューブ ジオメトリの Direct3D リソース。
            Microsoft::WRL::ComPtr<ID3D11InputLayout>   m_inputLayout;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_vertexBuffer1;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_vertexBuffer2;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_indexBuffer1;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_indexBuffer2;
            Microsoft::WRL::ComPtr<ID3D11VertexShader>  m_vertexShader;
            Microsoft::WRL::ComPtr<ID3D11PixelShader>   m_pixelShader;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_constantBuffer;
    
            // キューブ ジオメトリのシステム リソース。
            ModelViewProjectionConstantBuffer   m_constantBufferData1;
            ModelViewProjectionConstantBuffer   m_constantBufferData2;
            uint32  m_indexCount1;
            uint32  m_indexCount2;
            CreateColor     *model;
    
            // レンダリング ループで使用する変数。
            bool    m_loadingComplete;
            float   m_degreesPerSecond;
            bool    m_tracking;
        };
    }
    
  3. Sample3DSceneRenderer.cpp で二体のモデルを生成して描画します。
    Class CreateColor の CreateModel() 関数で二体のモデルを生成します。
    CreateWindowSizeDependentResources() で m_constantBufferData1 と m_constantBufferData2 に描画環境を設定します。
    モデル1は中央から左寄りに、モデル2は中央から右寄りに配置します。
    Rotate() 関数では、モデル1をY軸で,モデル2をX軸で回転します。
    Render() 関数ではモデル1に続いてモデル2を描画します。
    #include "pch.h"
    #include "Sample3DSceneRenderer.h"
    #include "..\Common\DirectXHelper.h"
    
    using namespace App1;
    using namespace DirectX;
    using namespace Windows::Foundation;
    
    // ファイルから頂点とピクセル シェーダーを読み込み、キューブのジオメトリをインスタンス化します。
    Sample3DSceneRenderer::Sample3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_loadingComplete(false),
        m_degreesPerSecond(45),
        m_indexCount1(0),
        m_indexCount2(0),
        m_tracking(false),
        m_deviceResources(deviceResources)
    {
        CreateDeviceDependentResources();
        CreateWindowSizeDependentResources();
    }
    
    // ウィンドウのサイズが変更されたときに、ビューのパラメーターを初期化します。
    void Sample3DSceneRenderer::CreateWindowSizeDependentResources()
    {
        Size outputSize = m_deviceResources->GetOutputSize();
        float aspectRatio = outputSize.Width / outputSize.Height;
        float fovAngleY = 70.0f * XM_PI / 180.0f;
    
        // アプリケーションが縦向きビューまたはスナップ ビュー内にあるときに行うことのできる変更の簡単な例です。
        if (aspectRatio < 1.0f) {  fovAngleY *= 2.0f;  }
    
        // このサンプルでは、行優先のマトリックスを使用した右辺座標系を使用しています。
        XMMATRIX perspectiveMatrix = XMMatrixPerspectiveFovRH(
            fovAngleY, aspectRatio, 0.01f, 100.0f);
    
        XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
        XMMATRIX orientationMatrix = XMLoadFloat4x4(&orientation);
    
        XMStoreFloat4x4(
            &m_constantBufferData1.projection,
            XMMatrixTranspose(perspectiveMatrix * orientationMatrix));
        XMStoreFloat4x4(
            &m_constantBufferData2.projection,
            XMMatrixTranspose(perspectiveMatrix * orientationMatrix));
    
        // 視点は (0,0.7,5.0) の位置にあり、y 軸に沿って上方向のポイント (0,-0.1,0) を見ています。
        static const XMVECTORF32 eye = { 0.0f, 0.7f, 5.0f, 0.0f };
        static const XMVECTORF32 at = { 1.0f, -0.1f, 0.0f, 0.0f };
        static const XMVECTORF32 at2 = { -1.0f, -0.1f, 0.0f, 0.0f };
        static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
    
        XMStoreFloat4x4(&m_constantBufferData1.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
        XMStoreFloat4x4(&m_constantBufferData2.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at2, up)));
    }
    
    // フレームごとに 1 回呼び出し、キューブを回転させてから、モデルおよびビューのマトリックスを計算します。
    void Sample3DSceneRenderer::Update(DX::StepTimer const& timer)
    {
        if (!m_tracking)
        {
            // 度をラジアンに変換し、秒を回転角度に変換します
            float radiansPerSecond = XMConvertToRadians(m_degreesPerSecond);
            double totalRotation = timer.GetTotalSeconds() * radiansPerSecond;
            float radians = static_cast<float>(fmod(totalRotation, XM_2PI));
    
            Rotate(radians);
        }
    }
    
    //3D キューブ モデルを、ラジアン単位で設定された大きさだけ回転させます。
    void Sample3DSceneRenderer::Rotate(float radians)
    {
        XMStoreFloat4x4(&m_constantBufferData1.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
        XMStoreFloat4x4(&m_constantBufferData2.model, XMMatrixTranspose(XMMatrixRotationX(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 つのフレームを描画します。
    void Sample3DSceneRenderer::Render()
    {
        // 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
        if (!m_loadingComplete) {  return;  }
    
        //☆モデル1を描画
        auto context = m_deviceResources->GetD3DDeviceContext();
        // 定数バッファーを準備して、グラフィックス デバイスに送信します。
        context->UpdateSubresource(
            m_constantBuffer.Get(), 0, NULL, &m_constantBufferData1, 0, 0);
        // 各頂点は、VertexPositionColor 構造体の 1 つのインスタンスです。
        UINT stride = sizeof(VertexPositionColor);
        UINT offset = 0;
        context->IASetVertexBuffers(
            0, 1, m_vertexBuffer1.GetAddressOf(), &stride, &offset);
        context->IASetIndexBuffer(
            m_indexBuffer1.Get(), DXGI_FORMAT_R16_UINT, 0);
        context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        context->IASetInputLayout(m_inputLayout.Get());
        // 頂点シェーダーをアタッチします。
        context->VSSetShader(m_vertexShader.Get(), nullptr, 0);
        // 定数バッファーをグラフィックス デバイスに送信します。
        context->VSSetConstantBuffers1(0, 1, m_constantBuffer.GetAddressOf(), nullptr, nullptr);
        // ピクセル シェーダーをアタッチします。
        context->PSSetShader(m_pixelShader.Get(), nullptr, 0);
        // オブジェクトを描画します。
        context->DrawIndexed(m_indexCount1, 0, 0);
    
        //☆モデル2を描画
        context = m_deviceResources->GetD3DDeviceContext();
        context->UpdateSubresource(
            m_constantBuffer.Get(), 0, NULL, &m_constantBufferData2, 0, 0);
        context->IASetVertexBuffers(
            0, 1, m_vertexBuffer2.GetAddressOf(), &stride, &offset);
        context->IASetIndexBuffer(
            m_indexBuffer2.Get(), DXGI_FORMAT_R16_UINT, 0);
        context->DrawIndexed(m_indexCount2, 0, 0);
    }
    
    void Sample3DSceneRenderer::CreateDeviceDependentResources()
    {
        // シェーダーを非同期で読み込みます。
        auto loadVSTask = DX::ReadDataAsync(L"SampleVertexShader.cso");
        auto loadPSTask = DX::ReadDataAsync(L"SamplePixelShader.cso");
        // 頂点シェーダー ファイルを読み込んだ後、シェーダーと入力レイアウトを作成します。
        auto createVSTask = loadVSTask.then([this](const std::vector<byte>& fileData) {
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateVertexShader(
                    &fileData[0], fileData.size(), nullptr, &m_vertexShader));
            static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
            {   { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
                { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            };
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateInputLayout(
                    vertexDesc, ARRAYSIZE(vertexDesc), &fileData[0], fileData.size(), &m_inputLayout));
        });
        // ピクセル シェーダー ファイルを読み込んだ後、シェーダーと定数バッファーを作成します。
        auto createPSTask = loadPSTask.then([this](const std::vector<byte>& fileData) {
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreatePixelShader(
                    &fileData[0], fileData.size(), nullptr, &m_pixelShader));
    
            CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer) , D3D11_BIND_CONSTANT_BUFFER);
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateBuffer(
                    &constantBufferDesc, nullptr, &m_constantBuffer));
        });
        // 両方のシェーダーの読み込みが完了したら、メッシュを作成します。
        auto createCubeTask = (createPSTask && createVSTask).then([this] () {
            model = new CreateColor(m_deviceResources);
            model->CreateModel(&m_vertexBuffer1, &m_indexBuffer1, &m_indexCount1);
            model->CreateModel(&m_vertexBuffer2, &m_indexBuffer2, &m_indexCount2);
        });
        // キューブが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this] () {  m_loadingComplete = true;  });
    }
    
    void Sample3DSceneRenderer::ReleaseDeviceDependentResources()
    {
        m_loadingComplete = false;
        m_vertexShader.Reset();
        m_inputLayout.Reset();
        m_pixelShader.Reset();
        m_constantBuffer.Reset();
        m_vertexBuffer1.Reset();
        m_vertexBuffer2.Reset();
        m_indexBuffer1.Reset();
        m_indexBuffer2.Reset();
    }
    
  4. コンパイル&実行すると二体のモデルが並んで描画されます。
    モデル1はY軸で、モデル2はX軸で回転しながら描画されます。

モデル1の周りをモデル2が回転

  1. モデル1とモデル2を親子関係に見立てて、モデル1の周りをモデル2が回転します。
    CreateWindowSizeDependentResources() 関数の描画環境を次のように設定します。
        static const XMVECTORF32 eye = { 0.0f, 0.7f, 5.0f, 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_constantBufferData1.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
        XMStoreFloat4x4(&m_constantBufferData2.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up)));
    
  2. Rotate() 関数で、モデル1の周りをモデル2が回転します。
    5.0f はモデル2の回転半径で、10.0f にすると大きく回転します。
    void Sample3DSceneRenderer::Rotate(float radians)
    {   XMMATRIX    view, mat;
        XMStoreFloat4x4(&m_constantBufferData1.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
        view = XMMatrixTranspose(XMMatrixLookAtRH(
            XMVECTORF32{ 0.0f, 0.0f, 5.0f, 0.0f }, XMVECTORF32{ 0.0f, 0.0f, 0.0f, 0.0f }, XMVECTORF32{ 0.0f, 1.0f, 0.0f, 0.0f }));
        mat = XMMatrixRotationView(0.8f, 0.0f, -radians);
        XMStoreFloat4x4(&m_constantBufferData2.model, mat * view);
    }
    
  3. プログラムを実行するとモデル1の周囲をモデルが回転します。
    親子孫で回転するプログラムは Win10 Model 3 を参照して下さい。

親・子・孫で回転します。
[Next Chapter ↓] X-FILE を親子で回転する
[Previous Chapter ↑] Win10 Create Color

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