Win10 TextureBox

Windows10 DirectX3D で Texture を貼り付けた BOX を定義して描画します。

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

プロジェクトの作成

  1. DirectX3D でテクスチャを貼り付けた立方体を描画します。
    プログラムは大きいのですが、全ソースコードを掲載しているので作成するのは簡単です。
    Win10 Template を参照して DirectX 11 アプリ(ユニバーサル Windows)を構築して下さい。
    カラーキューブが回転しながら描画され、FPS が表示されます。
    プロジェクトの名前は App1 になっています。
  2. テクスチャを貼り付けた立方体を描画するので、モデルの定義やシェーダが全面的に変わります。
    シェーダーの説明は Windows10 Shader から「頂点座標+法線+テクスチャ」を参照して下さい。
  3. Content\Sample3DSceneRenderer.cpp のソースコードを修正します。
    次のファイルを Content\ のファイルに上書きして下さい。
    ソースコードは、このページの後半に掲載します。
    Content\Sample3DSceneRenderer.h
    Content\Sample3DSceneRenderer.cpp
  4. 次のファイルを Content\ に格納して下さい。
    App1/Content を選択して[追加][既存の項目]からプロジェクトに追加します。
    これらのファイルはテクスチャを貼り付ける為に使用します。
    ソースコードは Windows10 Shader に掲載しています。
    Content\BasicReaderWriter.h
    Content\BasicReaderWriter.cpp
    Content\TextureLoader.h
    Content\TextureLoader.cpp
  5. テクスチャ画像(star.jpg)を App1\star.jpg にコピーしてプロジェクトに加えます。
    テクスチャ画像は何でも良いので、適当なものを調達してきて下さい。
    コンパイル&実行するとテクスチャを貼り付けた立方体が回転しながら描画されます。
    同様のプログラムを Texture に掲載しているので参考にして下さい。

ソースコード

  1. Win10 Triangle のモデルは「頂点座標+カラー」で定義されていました。
    Win10 NormBox のモデルは「頂点座標+法線ベクトル」で定義されていました。
    今回のモデルは「頂点座標+法線ベクトル+テクスチャ」で定義しています。
    モデルの定義及び、描画するシェーダも全面的に変わるので注意して下さい。
  2. Sample3DSceneRenderer.h です。
    #include "ShaderStructures.h" でヘッダーを取り込みます。
    #pragma once
    
    #include "..\Common\DeviceResources.h"
    #include "..\Common\StepTimer.h"
    #include "ShaderStructures.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_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;
    
            Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_textureSRV;
            Microsoft::WRL::ComPtr<ID3D11SamplerState>  m_sampler;
    
        };
    }
    
  3. Sample3DSceneRenderer.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 },
                { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            };
    
    VertexPosition modelVertices[] で立方体の頂点データを定義します。
    立方体の頂点は8個ですが「座標, 法線, テクスチャ」のいずれかが違えば別の頂点データとして定義します。
    unsigned short modelIndices[] で三角形を組み合わせて立方体のモデルを作成します。
    // テクスチャモデルを描画
    #include "pch.h"
    #include "Sample3DSceneRenderer.h"
    
    #include "..\Common\DirectXHelper.h"
    #include "TextureLoader.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_indexCount(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;
        }
    
        // 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,2.0) の位置にあり、y 軸に沿って上方向のポイント (0,-0.1,0) を見ています。
        static const XMVECTORF32 eye = { 0.0f, 0.7f, 2.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_constantBufferData.view, XMMatrixTranspose(XMMatrixLookAtRH(eye, at, 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_constantBufferData.model, XMMatrixTranspose(XMMatrixRotationY(radians)));
    }
    
    void Sample3DSceneRenderer::StartTracking()
    {
        m_tracking = true;
    }
    
    // 追跡時に、出力画面の幅方向を基準としてポインターの位置を追跡することにより、モデルを 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;
        }
    
        auto context = m_deviceResources->GetD3DDeviceContext();
    
        // レンダー ターゲットを画面に設定します。
        ID3D11RenderTargetView *const targets[1] = { m_deviceResources->GetBackBufferRenderTargetView() };
        context->OMSetRenderTargets(1, targets, m_deviceResources->GetDepthStencilView());
    
        // 定数バッファーを準備して、グラフィックス デバイスに送信します。
        context->UpdateSubresource(
            m_constantBuffer.Get(),
            0,
            NULL,
            &m_constantBufferData,
            0,
            0
            );
    
        // 各頂点は、VertexPositionColor 構造体の 1 つのインスタンスです。
        //UINT stride = sizeof(VertexPositionColor);
        UINT stride = sizeof(VertexPosition);
        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->PSSetShaderResources(0, 1, m_textureSRV.GetAddressOf());
    
        context->PSSetShaderResources(0, 1, m_textureSRV.GetAddressOf());
        context->PSSetSamplers(0, 1, m_sampler.GetAddressOf());
    
        // オブジェクトを描画します。
        context->DrawIndexed(
            m_indexCount,
            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 },
                { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
                { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, 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 VertexPosition modelVertices[] = 
        {
            { XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT2(0.0f, 0.0f) }, // +Y (top face)
            { XMFLOAT3( 0.5f, 0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) },
            { XMFLOAT3( 0.5f, 0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(-0.5f, 0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) },
    
            { XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f), XMFLOAT2(0.0f, 0.0f) }, // -Y (bottom face)
            { XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) },
            { XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, -1.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) },
    
            { XMFLOAT3(0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT2(0.0f, 0.0f) }, // +X (right face)
            { XMFLOAT3(0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) },
            { XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) },
    
            { XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f), XMFLOAT2(0.0f, 0.0f) }, // -X (left face)
            { XMFLOAT3(-0.5f,  0.5f,  0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) },
            { XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(-1.0f, 0.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) },
    
            { XMFLOAT3(-0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 0.0f) }, // +Z (front face)
            { XMFLOAT3( 0.5f,  0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 0.0f) },
            { XMFLOAT3( 0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f), XMFLOAT2(0.0f, 1.0f) },
    
            { XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f), XMFLOAT2(0.0f, 0.0f) }, // -Z (back face)
            { XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f), XMFLOAT2(1.0f, 0.0f) },
            { XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f), XMFLOAT2(1.0f, 1.0f) },
            { XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, -1.0f), XMFLOAT2(0.0f, 1.0f) },
        };
    
            D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
            vertexBufferData.pSysMem = modelVertices;
            vertexBufferData.SysMemPitch = 0;
            vertexBufferData.SysMemSlicePitch = 0;
            CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(modelVertices), D3D11_BIND_VERTEX_BUFFER);
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateBuffer(
                    &vertexBufferDesc,
                    &vertexBufferData,
                    &m_vertexBuffer
                    )
                );
    
        // モデルの頂点 Index を定義
        unsigned short modelIndices[] = 
        {
            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(modelIndices);
    
            D3D11_SUBRESOURCE_DATA indexBufferData = {0};
            indexBufferData.pSysMem = modelIndices;
            indexBufferData.SysMemPitch = 0;
            indexBufferData.SysMemSlicePitch = 0;
            CD3D11_BUFFER_DESC indexBufferDesc(sizeof(modelIndices), D3D11_BIND_INDEX_BUFFER);
            DX::ThrowIfFailed(
                m_deviceResources->GetD3DDevice()->CreateBuffer(
                    &indexBufferDesc,
                    &indexBufferData,
                    &m_indexBuffer
                    )
                );
        });
    
        // モデルが読み込まれたら、オブジェクトを描画する準備が完了します。
        createCubeTask.then([this] () {
            m_loadingComplete = true;
        });
    
        TextureLoader^ loader = ref new TextureLoader(m_deviceResources->GetD3DDevice());
        loader->LoadTexture(
            L"Star.jpg",
            nullptr,
            &m_textureSRV );
    
        // create the sampler
        D3D11_SAMPLER_DESC samplerDesc;
        ZeroMemory(&samplerDesc, sizeof(D3D11_SAMPLER_DESC));
        samplerDesc.Filter = D3D11_FILTER_ANISOTROPIC;
        samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
        samplerDesc.MipLODBias = 0.0f;
        samplerDesc.MaxAnisotropy = 2;
        samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
        samplerDesc.BorderColor[0] = 0.0f;
        samplerDesc.BorderColor[1] = 0.0f;
        samplerDesc.BorderColor[2] = 0.0f;
        samplerDesc.BorderColor[3] = 0.0f;
        samplerDesc.MinLOD = 0;
        samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
    
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateSamplerState(
                &samplerDesc, &m_sampler) );
    }
    
    void Sample3DSceneRenderer::ReleaseDeviceDependentResources()
    {
        m_loadingComplete = false;
        m_vertexShader.Reset();
        m_inputLayout.Reset();
        m_pixelShader.Reset();
        m_constantBuffer.Reset();
        m_vertexBuffer.Reset();
        m_indexBuffer.Reset();
    }
    

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