【DX12を学びたい】DirectXTexで画像の読み込み、作成、表示まで

テクスチャの表示 DirextX

諸々の設定を済ませたので、ようやく表示対応を行います。

P1:DirectXTexの準備
P2:画像の読み込み、リソースの作成とコピー
  シェーダーリソースビューの作成
P3:各所の設定
P4:テクスチャ表示

スポンサーリンク

テクスチャ表示

テクスチャ表示用のSpriteクラスを用意します。

 

ヘッダーファイル

#pragma once

#include "Texture.h"

// 画像表示のクラス
class Sprite
{
public:
	Sprite(float fOffset);
	~Sprite();

	// 初期化
	bool Initialize(ID3D12Device* pDevice, const wchar_t* pFile);
	// 更新
	void Update(ID3D12GraphicsCommandList* pCommandList);
	// 描画
	void Draw(ID3D12GraphicsCommandList* pCommandList);

private:
	// バッファーの作成
	bool CreateBuffer(ID3D12Device* pDevice);
	// ディスクリプタヒープの作成
	bool CreateDescriptorHeap(ID3D12Device* pDevice);
	// マップ
	bool Map();
	// バッファービューの設定
	void SetBufferView();

private:
	struct Vertex
	{
		DirectX::XMFLOAT3 Pos;
		DirectX::XMFLOAT4 Color;
		DirectX::XMFLOAT2 Uv;
	};
	Vertex m_Vertices[4];
	unsigned short m_IndexData[6];

	ID3D12Resource* m_pVertexBuffer;
	ID3D12Resource* m_pIndexBuffer;

	D3D12_VERTEX_BUFFER_VIEW m_VertexBufferView;
	D3D12_INDEX_BUFFER_VIEW m_IndexBufferView;

	Texture* m_pTexture;

	ID3D12DescriptorHeap* m_pDescHeap;
};

 

C++ファイル

コンストラクタのfOffsetは位置を変更するために仮で追加したものなので、無くてもいいです。

テクスチャクラスを作成して、シェーダーリソースビューの作成をしておきます。

続いて、頂点とインデックスバッファーの作成、データのMap、バッファービューを設定したら四角形表示の準備は完了です。

#include "Sprite.h"

// コンストラクタ
Sprite::Sprite(float fOffset)
    : m_IndexData{ 0,1,2,0,2,3 }
    , m_pVertexBuffer(nullptr)
    , m_pIndexBuffer(nullptr)
    , m_pTexture(nullptr)
    , m_pDescHeap(nullptr)
{
    // 頂点情報
    m_Vertices[0].Pos = DirectX::XMFLOAT3(-0.15f + fOffset, 0.25f, 0.0f);
    m_Vertices[1].Pos = DirectX::XMFLOAT3(0.15f + fOffset, 0.25f, 0.0f);
    m_Vertices[2].Pos = DirectX::XMFLOAT3(0.15f + fOffset, -0.25f, 0.0f);
    m_Vertices[3].Pos = DirectX::XMFLOAT3(-0.15f + fOffset, -0.25f, 0.0f);
    m_Vertices[0].Color = DirectX::XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_Vertices[1].Color = DirectX::XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_Vertices[2].Color = DirectX::XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_Vertices[3].Color = DirectX::XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_Vertices[0].Uv = DirectX::XMFLOAT2(0.0f, 0.0f);
    m_Vertices[1].Uv = DirectX::XMFLOAT2(1.0f, 0.0f);
    m_Vertices[2].Uv = DirectX::XMFLOAT2(1.0f, 1.0f);
    m_Vertices[3].Uv = DirectX::XMFLOAT2(0.0f, 1.0f);

    // バッファービュー
    memset(&m_VertexBufferView, 0, sizeof(m_VertexBufferView));
    memset(&m_IndexBufferView, 0, sizeof(m_IndexBufferView));
}

// デストラクタ
Sprite::~Sprite()
{
    SAFE_DELETE(m_pTexture);
    SAFE_RELEASE(m_pIndexBuffer);
    SAFE_RELEASE(m_pVertexBuffer);
    SAFE_RELEASE(m_pDescHeap);
}

// 初期化
bool Sprite::Initialize(ID3D12Device* pDevice, const wchar_t* pFile)
{
    // ディスクリプタヒープの作成
    CreateDescriptorHeap(pDevice);
    
    // テクスチャ(リソース)の作成
    m_pTexture = new Texture();
    if (m_pTexture->CreateTexture(pDevice, pFile) == false) {
        return false;
    }
    // SRVを作成
    m_pTexture->CreateSRV(pDevice, m_pDescHeap->GetCPUDescriptorHandleForHeapStart());


    if (CreateBuffer(pDevice) == false) {
        return false;

    }
    if (Map() == false) {
        return false;

    }

    SetBufferView();

    return true;
}

// 更新
void Sprite::Update(ID3D12GraphicsCommandList* pCommandList)
{
    if (m_pTexture) {
        m_pTexture->Update(pCommandList);
    }
}

// ディスクリプタヒープの作成
bool Sprite::CreateDescriptorHeap(ID3D12Device* pDevice)
{
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
    // 1つ
    heapDesc.NumDescriptors = 1;
    // SRV用
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    // シェーダーから見える
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;

    // ディスクリプタヒープを作成
    HRESULT result = pDevice->CreateDescriptorHeap(
        &heapDesc,
        IID_PPV_ARGS(&m_pDescHeap)
    );
    if (result == S_FALSE)  return false;

    return true;
}

// リソースの作成
bool Sprite::CreateBuffer(ID3D12Device* pDevice)
{
    if (pDevice == nullptr) return false;

    const UINT vertexBufferSize = sizeof(m_Vertices);

    // ヒープのプロパティ
    D3D12_HEAP_PROPERTIES prop;
    // ヒープの種類
    prop.Type = D3D12_HEAP_TYPE_UPLOAD;     // cpuは書き込み、gpuは読み取り
    // CPUページプロパティ
    prop.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; // プロパティ不明
    // メモリプール
    prop.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;  // プール不明
    // マルチアダプター関連
    prop.CreationNodeMask = 1;
    prop.VisibleNodeMask = 1;

    // リソースの設定
    D3D12_RESOURCE_DESC desc;
    // リソースのディメンション
    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    // 配置の指定
    desc.Alignment = 0;
    // リソースの幅、高さ(今回はバッファーサイズ)
    desc.Width = vertexBufferSize;
    desc.Height = 1;
    // リソース深さ
    desc.DepthOrArraySize = 1;
    // MIPレベル
    desc.MipLevels = 1;
    // リソースデータの形式
    desc.Format = DXGI_FORMAT_UNKNOWN;  // バッファーはこれでいい
    // マルチサンプリングの設定
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    // テクスチャのレイアウト
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;   // データを連続して配置する
    // オプション
    desc.Flags = D3D12_RESOURCE_FLAG_NONE;  // なし

    // 頂点バッファーを作成
    HRESULT result = pDevice->CreateCommittedResource(
        &prop,
        D3D12_HEAP_FLAG_NONE,               // オプションなし
        &desc,
        D3D12_RESOURCE_STATE_GENERIC_READ,  // 読み取り状態
        nullptr,                            // クリアカラー
        IID_PPV_ARGS(&m_pVertexBuffer)
    );
    IS_S_FALSE(result);


    // インデックス用にサイズだけ変更
    desc.Width = sizeof(m_IndexData);

    // インデックスバッファーを作成
    result = pDevice->CreateCommittedResource(
        &prop,
        D3D12_HEAP_FLAG_NONE,               // オプションなし
        &desc,
        D3D12_RESOURCE_STATE_GENERIC_READ,  // 読み取り状態
        nullptr,                            // クリアカラー
        IID_PPV_ARGS(&m_pIndexBuffer)
    );
    IS_S_FALSE(result);

    return true;
}

// マップ
bool Sprite::Map()
{
    UINT bufferSize = sizeof(m_Vertices);

    // リソースデータのポインターを取得
    UINT8* pDataBegin;
    HRESULT result = m_pVertexBuffer->Map(
        0,          // インデックス番号
        nullptr,    // リソース全体
        reinterpret_cast<void**>(&pDataBegin)
    );
    IS_S_FALSE(result);
    // バッファーに情報をコピー
    memcpy(pDataBegin, m_Vertices, bufferSize);
    // 取得したポインターを無効にする
    m_pVertexBuffer->Unmap(0, nullptr);

    // インデックス
    bufferSize = sizeof(m_IndexData);
    result = m_pIndexBuffer->Map(
        0,          // インデックス番号
        nullptr,    // リソース全体
        reinterpret_cast<void**>(&pDataBegin)
    );
    IS_S_FALSE(result);
    // バッファーに情報をコピー
    memcpy(pDataBegin, m_IndexData, bufferSize);
    // 取得したポインターを無効にする
    m_pIndexBuffer->Unmap(0, nullptr);

    return true;
}

// 頂点バッファービューの設定
void Sprite::SetBufferView()
{
    // バッファーの仮想アドレス
    m_VertexBufferView.BufferLocation = m_pVertexBuffer->GetGPUVirtualAddress();
    // 1頂点のバイトサイズ
    m_VertexBufferView.StrideInBytes = sizeof(m_Vertices[0]);
    // バッファーのバイトサイズ
    m_VertexBufferView.SizeInBytes = sizeof(m_Vertices);

    // インデックス
    // バッファーの仮想アドレス
    m_IndexBufferView.BufferLocation = m_pIndexBuffer->GetGPUVirtualAddress();
    // バッファーのフォーマット
    m_IndexBufferView.Format = DXGI_FORMAT_R16_UINT;
    // バッファーのバイトサイズ
    m_IndexBufferView.SizeInBytes = sizeof(m_IndexData);
}

 

描画

いよいよ描画ですが、テクスチャ描画はシェーダーリソースビューを作成するときにセットした、ディスクリプタヒープを使用します。

そのヒープを下記の関数にセットすればOKです。
・SetDescriptorHeaps
・SetGraphicsRootDescriptorTable

// 描画
void Sprite::Draw(ID3D12GraphicsCommandList* pCommandList)
{
    if (pCommandList == nullptr || m_pTexture == nullptr) return;
    if (!m_pTexture->IsCopied())  return;

    // ディスクリプタヒープの設定
    ID3D12DescriptorHeap* ppHeaps[] = { m_pDescHeap };
    pCommandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
    
    // ルートパラメータにディスクリプタヒープのハンドルを設定
    pCommandList->SetGraphicsRootDescriptorTable(
        0,    // ルートパラメータのインデックス
        m_pDescHeap->GetGPUDescriptorHandleForHeapStart()
    );

    // 3点で1面の情報
    pCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    // 頂点バッファービューの設定
    pCommandList->IASetVertexBuffers(
        0,                  // インデックス
        1,                  // ビューの数
        &m_VertexBufferView
    );
    // インデックスバッファーの設定
    pCommandList->IASetIndexBuffer(&m_IndexBufferView);
    // インデックスで描画
    pCommandList->DrawIndexedInstanced(
        _countof(m_IndexData),  // インデックスの数
        1,                      // 描画するインスタンスの数
        0,                      // 最初のインデックス
        0,                      // 各インデックスに追加する頂点の値
        0                       // 一旦0
    );
}

 

さいごに

テクスチャリソースのデータコピーにCopyTextureRegionを使用しましたが、WriteToSubresourceを使うやり方だと、よりシンプルで分かりやすくなります。問題なさそうならこちらで実装しても良いでしょう。(サンプルがCopyTextureRegionを使っていたので、こちらを選びました)

今回のSpriteクラスは同じ物を描画させると、テクスチャを重複して作ったり、まだまだ改善の余地はありますが、とりあえず画像は出せた!

ということで、次は透過処理に対応させましょう!

かれいど

ゲームをしたり、作ったり
色々な事に挑戦していきたい!

サッカー観戦も趣味で
主にJ1リーグを観ています。

かれいどをフォローする
DirextX
スポンサーリンク
シェアする

コメント