諸々の設定を済ませたので、ようやく表示対応を行います。
テクスチャ表示
テクスチャ表示用の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クラスは同じ物を描画させると、テクスチャを重複して作ったり、まだまだ改善の余地はありますが、とりあえず画像は出せた!
ということで、次は透過処理に対応させましょう!
コメント