Win10 NormBox

Win10 NormBox

Windows10 DirectX3D で法線を設定した立方体を定義して描画します。

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

プロジェクトの作成

  1. DirectX3D で法線ベクトルを設定した立方体を描画します。
    Win10 Base (Windows10 空のプロジェクト)をコピーして下さい。
    プロジェクトの名前は App1 になっています。
  2. 次のファイルを Content\ に格納して下さい。
    App1 を選択して[追加][既存の項目]からプロジェクトに追加します。
    Box3DSceneRenderer は自動生成される Sample3DSceneRenderer を元に修正したものです。
    ソースコードは、このページの後半に掲載します。
    シェーダーの説明は Win10 Shader から「頂点座標+法線」を参照して下さい。
    シェーダの名前は BoxPixelShader.hlsl, BoxVertexShader.hlsl を使っています。
    Content\Box3DSceneRenderer.h
    Content\Box3DSceneRenderer.cpp
    Content\BoxShader.h
    Content\BoxPixelShader.hlsl
    Content\BoxVertexShader.hlsl
  3. App1Main.h を選択して Box3DSceneRenderer.h を取り込んで、m_sceneRenderer を定義します。
    #include "Content\Box3DSceneRenderer.h"
    
            ・・・
    
            // TODO: これを独自のコンテンツ レンダラーで置き換えます。
            std::unique_ptr<Box3DSceneRenderer> m_sceneRenderer;
    
  4. App1Main.cpp で Box3DSceneRenderer Class を使って立方体を描画します。
    Box3DSceneRenderer() から m_sceneRenderer を生成します。
        m_sceneRenderer = std::unique_ptr<Box3DSceneRenderer>(new Box3DSceneRenderer(m_deviceResources));
    
    DirectX 11 アプリ(ユニバーサル Windows)を参照して m_sceneRenderer 関係のソースコードを追加して下さい。
  5. コンパイル&実行してすると法線ベクトルを設定した立方体が回転しながら描画されます。
    同様のプログラムを 法線ベクトル に掲載しているので参考にして下さい。

ソースコード

  1. Win10 Triangle のモデルは「頂点座標+カラー」で定義されていましたが、今回のモデルは「頂点座標+法線ベクトル」で定義します。
    それに伴い、モデルを定義するソースコード及び、モデルを描画するシェーダも Win10 Triangle とは異なるので注意して下さい。
  2. BoxShader.h です。
    model; view; projection; は XMFLOAT4X4 で、pos, norm は XMFLOAT3 で定義します。
    シェーダーは Win10 Shader から「頂点座標+法線」のものを使用して下さい。
    #pragma once
    
    namespace App1
    {
        // MVP マトリックスを頂点シェーダーに送信するために使用する定数バッファー。
        struct ModelViewProjectionConstantBuffer
        {
            DirectX::XMFLOAT4X4 model;
            DirectX::XMFLOAT4X4 view;
            DirectX::XMFLOAT4X4 projection;
        };
    
        // 頂点シェーダーへの頂点ごとのデータの送信に使用します。
        struct VertexPositionNorm
        {
            DirectX::XMFLOAT3 pos;
            DirectX::XMFLOAT3 norm;
        };
    }
    
  3. Box3DSceneRenderer.h です。
    #include "BoxShader.h" でヘッダーを取り込みます。
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "..\Common\StepTimer.h"
    #include "BoxShader.h"
    
    namespace App1
    {
        class Box3DSceneRenderer
        {
        public:
            Box3DSceneRenderer(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;
    
            Microsoft::WRL::ComPtr<ID3D11InputLayout>   m_inputLayout;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_vertexBuffer;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_indexBuffer;
            Microsoft::WRL::ComPtr<ID3D11VertexShader>  m_vertexShader;
            Microsoft::WRL::ComPtr<ID3D11PixelShader>   m_pixelShader;
            Microsoft::WRL::ComPtr<ID3D11Buffer>        m_constantBuffer;
    
            ModelViewProjectionConstantBuffer   m_constantBufferData;
            uint32  m_indexCount;
    
            bool    m_loadingComplete;
            float   m_degreesPerSecond;
            bool    m_tracking;
        };
    }
    
  4. Box3DSceneRenderer.cpp です。
    頂点データの形式は、頂点座標と法線ベクトルです。
            static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
            {
                { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
                { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            };
    
    VertexPositionNorm BoxVertices[] で立方体の頂点データを定義します。
    頂点座標が同じでも法線ベクトルが違えば別のデータとして定義します。
    unsigned short BoxIndices [] で三角形を組み合わせて立方体のモデルを作成します。
    #include "pch.h"
    #include "Box3DSceneRenderer.h"
    #include "..\Common\DirectXHelper.h"
    
    using namespace App1;
    using namespace DirectX;
    using namespace Windows::Foundation;
    
    Box3DSceneRenderer::Box3DSceneRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_loadingComplete(false),
        m_degreesPerSecond(45),
        m_indexCount(0),
        m_tracking(false),
        m_deviceResources(deviceResources)
    {
        CreateDeviceDependentResources();
        CreateWindowSizeDependentResources();
    }
    
    // ウィンドウのサイズが変更されたときに、ビューのパラメーターを初期化します。
    void Box3DSceneRenderer::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_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 Box3DSceneRenderer::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 Box3DSceneRenderer::Rotate(float radians)
    {
        //更新されたモデル マトリックスをシェーダーに渡す準備をします
        XMStoreFloat4x4(&m_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
    }
    
    void Box3DSceneRenderer::StartTracking()
    {
        m_tracking = true;
    }
    
    // 3D モデルを Y 軸に沿って回転させることができます。
    void Box3DSceneRenderer::TrackingUpdate(float positionX)
    {
        if (m_tracking)
        {
            float radians = XM_2PI * 2.0f * positionX / m_deviceResources->GetOutputSize().Width;
            Rotate(radians);
        }
    }
    
    void Box3DSceneRenderer::StopTracking()
    {
        m_tracking = false;
    }
    
    // 頂点とピクセル シェーダーを使用して、1 つのフレームを描画します。
    void Box3DSceneRenderer::Render()
    {
        // 読み込みは非同期です。読み込みが完了した後にのみ描画してください。
        if (!m_loadingComplete)
        {
            return;
        }
    
        auto context = m_deviceResources->GetD3DDeviceContext();
    
        // 定数バッファーを準備して、グラフィックス デバイスに送信します。
        context->UpdateSubresource(
            m_constantBuffer.Get(),
            0,
            NULL,
            &m_constantBufferData,
            0,
            0
            );
    
        // 各頂点は、VertexPositionNorm 構造体の 1 つのインスタンスです。
        UINT stride = sizeof(VertexPositionNorm);
        UINT offset = 0;
        context->IASetVertexBuffers(
            0,
            1,
            m_vertexBuffer.GetAddressOf(),
            &stride,
            &offset
            );
    
        context->IASetIndexBuffer(
            m_indexBuffer.Get(),
            DXGI_FORMAT_R16_UINT, //各インデックスは、1 つの 16 ビット符号なし整数 (short) です。
            0
            );
    
        context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    
        context->IASetInputLayout(m_inputLayout.Get());
    
        // 頂点シェーダーをアタッチします。
        context->VSSetShader(
            m_vertexShader.Get(),
            nullptr,
            0
            );
    
        // 定数バッファーをグラフィックス デバイスに送信します。
        context->VSSetConstantBuffers(
            0,
            1,
            m_constantBuffer.GetAddressOf()
            );
    
        // ピクセル シェーダーをアタッチします。
        context->PSSetShader(
            m_pixelShader.Get(),
            nullptr,
            0
            );
    
        // オブジェクトを描画します。
        context->DrawIndexed(
            m_indexCount,
            0,
            0
            );
    }
    
    void Box3DSceneRenderer::CreateDeviceDependentResources()
    {
        // シェーダーを非同期で読み込みます。
        auto loadVSTask = DX::ReadDataAsync(L"BoxVertexShader.cso");
        auto loadPSTask = DX::ReadDataAsync(L"BoxPixelShader.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 },
                { "NORMAL",   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] () {
    
            // メッシュの頂点を読み込みます。各頂点には、位置と法線ベクトルが定義されています。
            static const VertexPositionNorm BoxVertices[] = 
            {
                { XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, // +Y (top face)
                { 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, 0.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, 0.0f) }, // -Y (bottom face)
                { 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, 0.0f) },
                { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f) },
    
                { XMFLOAT3(0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, // +X (right face)
                { 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, 0.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, 0.0f) }, // -X (left face)
                { 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, 0.0f) },
                { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f) },
    
                { XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, // +Z (front face)
                { XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.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, 0.0f, 1.0f) },
    
                { XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f) }, // -Z (back face)
                { XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.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, 0.0f, -1.0f) },
            };
    
            D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
            vertexBufferData.pSysMem = BoxVertices;
            vertexBufferData.SysMemPitch = 0;
            vertexBufferData.SysMemSlicePitch = 0;
            CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(BoxVertices), D3D11_BIND_VERTEX_BUFFER);
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateBuffer(
                    &vertexBufferDesc,
                    &vertexBufferData,
                    &m_vertexBuffer
                    )
                );
    
            // メッシュのインデックスを読み込みます。
            // インデックスの 3 つ 1 組で画面上に描画される三角形を表します。
            static const unsigned short BoxIndices [] =
            {
                0, 1, 2,      0, 2, 3,
                4, 5, 6,      4, 6, 7,
                8, 9, 10,     8, 10, 11,
                12, 13, 14,   12, 14, 15,
                16, 17, 18,   16, 18, 19,
                20, 21, 22,   20, 22, 23
            };
    
            m_indexCount = ARRAYSIZE(BoxIndices);
    
            D3D11_SUBRESOURCE_DATA indexBufferData = {0};
            indexBufferData.pSysMem = BoxIndices;
            indexBufferData.SysMemPitch = 0;
            indexBufferData.SysMemSlicePitch = 0;
            CD3D11_BUFFER_DESC indexBufferDesc(sizeof(BoxIndices), D3D11_BIND_INDEX_BUFFER);
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateBuffer(
                    &indexBufferDesc,
                    &indexBufferData,
                    &m_indexBuffer
                    )
                );
        });
    
        // キューブが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this] () {
            m_loadingComplete = true;
        });
    }
    
    void Box3DSceneRenderer::ReleaseDeviceDependentResources()
    {
        m_loadingComplete = false;
        m_vertexShader.Reset();
        m_inputLayout.Reset();
        m_pixelShader.Reset();
        m_constantBuffer.Reset();
        m_vertexBuffer.Reset();
        m_indexBuffer.Reset();
    }
    

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