DirectXTexの準備ができたら、画像を読み込んでデータを取得し、描画に必要なリソースを作成していきます。
読み込みとリソース作成
テクスチャ用のクラスを用意するので、cppとhファイルを追加します。
ヘッダーファイル
#pragma once
#include <d3d12.h>
// 画像のクラス
class Texture
{
// 状態
enum
{
STATE_INIT = 0,
STATE_COPY,
STATE_COPY_WAIT,
STATE_IDLE,
};
public:
Texture();
~Texture();
// 更新
void Update(ID3D12GraphicsCommandList* pCommandList);
// テクスチャーの作成
bool CreateTexture(ID3D12Device* pDevice, const wchar_t* pFile);
// シェーダーリソースビューの作成
void CreateSRV(ID3D12Device* pDevice, D3D12_CPU_DESCRIPTOR_HANDLE handle);
// コピー済みか
bool IsCopied() const { return m_State > STATE_INIT; }
private:
// テクスチャ情報をコピー
void CopyTexture(ID3D12GraphicsCommandList* pCommandList);
private:
int m_Count;
int m_State;
// リソース情報
size_t m_RowPitch;
ID3D12Resource* m_pTexture;
ID3D12Resource* m_pTextureUpload;
};
C++ファイル
初期化と開放です。
DirectXTex.hのインクルードでエラーが出なければOKです。
#include "Texture.h"
#include "DirectXTex/DirectXTex.h"
// コンストラクタ
Texture::Texture()
: m_Count(0)
, m_State(STATE_INIT)
, m_RowPitch(0)
, m_pTexture(nullptr)
, m_pTextureUpload(nullptr)
{
}
// デストラクタ
Texture::~Texture()
{
SAFE_RELEASE(m_pTextureUpload);
SAFE_RELEASE(m_pTexture);
}
画像を読み込む
LoadFromWICFileで画像ファイルを読み込みます。
問題なくロードが完了すると、TexMetadataとScratchImageに画像情報がセットされます。
// テクスチャーの作成
bool Texture::CreateTexture(ID3D12Device* pDevice, const wchar_t* pFile)
{
DirectX::TexMetadata metadata = {};
DirectX::ScratchImage scratchImg = {};
// ファイルの読み込み
HRESULT result = DirectX::LoadFromWICFile(
pFile,
DirectX::WIC_FLAGS_NONE, // オプションなし
&metadata,
scratchImg
);
IS_S_FALSE(result);
const DirectX::Image* pImage = scratchImg.GetImage(0, 0, 0);
// 画像情報を保持
m_RowPitch = pImage->rowPitch;
//
// リソース作成へ続く
//
// コピーへ移行
m_State = STATE_COPY;
return true;
}
リソースを作成
テクスチャ情報のリソース作成はCreateCommittedResourceを使用します。
D3D12_HEAP_PROPERTIES、D3D12_RESOURCE_DESCをテクスチャ用に設定し、リソースを作成します。
今回はCopyTextureRegionを使用するので、リソースが2つ必要になります。
1:テクスチャとして使用するリソース
2:1のリソースに情報をコピーするためのリソース
アップロードリソース
リソースデータをコピーするためのリソースです。
頂点バッファーのような感じで、データ用のリソースを作成します。
// ヒープのプロパティ
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 = pImage->slicePitch;
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; // なし
// データアップロード用のリソースを作成
result = pDevice->CreateCommittedResource(
&prop,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_pTextureUpload)
);
IS_S_FALSE(result);
UINT8* pDataBegin;
// リソースデータのポインターを取得
result = m_pTextureUpload->Map(
0, // インデックス番号
nullptr, // リソース全体
reinterpret_cast<void**>(&pDataBegin)
);
IS_S_FALSE(result);
// バッファーに情報をコピー
memcpy(pDataBegin, pImage->pixels, pImage->slicePitch);
// 取得したポインターを無効にする
m_pTextureUpload->Unmap(0, nullptr);
// テクスチャリソースの作成へ続く
テクスチャリソース
こちらはテクスチャの情報を設定してリソースの作成します。
中身はコピーで持ってくるので、D3D12_RESOURCE_STATE_COPY_DESTを設定しています。
// ヒープの設定
prop.Type = D3D12_HEAP_TYPE_DEFAULT;
// リソースの設定
desc.Dimension = static_cast<D3D12_RESOURCE_DIMENSION>(metadata.dimension);
desc.Width = static_cast<UINT64>(metadata.width);
desc.Height = static_cast<UINT>(metadata.height);
desc.DepthOrArraySize = static_cast<UINT16>(metadata.arraySize);
desc.MipLevels = static_cast<UINT16>(metadata.mipLevels);
desc.Format = metadata.format;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
// テクスチャのリソースを作成
result = pDevice->CreateCommittedResource(
&prop,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_pTexture)
);
IS_S_FALSE(result);
// シェーダーリソースビューの作成へ続く
※propとdescはアップロードリソースの物をそのまま使用
リソースデータのコピー
リソースの作成は完了しましたが、テクスチャリソースのデータが空のままです。
ということで、コピー処理のCopyTextureRegionが必要になります。
CommandListから呼べる関数なので、処理を追加しておきましょう。
// テクスチャ情報をコピー
void Texture::CopyTexture(ID3D12GraphicsCommandList* pCommandList)
{
if (pCommandList == nullptr || m_pTexture == nullptr) {
return;
}
D3D12_RESOURCE_DESC desc = m_pTexture->GetDesc();
// テクスチャのコピー情報
D3D12_TEXTURE_COPY_LOCATION dest;
// コピー先
dest.pResource = m_pTexture;
// 種類
dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
// サブリソースのインデックス
dest.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION src;
// コピー元
src.pResource = m_pTextureUpload;
// 種類
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
// オフセット
src.PlacedFootprint.Offset = 0;
// リソースの情報
src.PlacedFootprint.Footprint.Format = desc.Format;
src.PlacedFootprint.Footprint.Width = static_cast<UINT>(desc.Width);
src.PlacedFootprint.Footprint.Height = static_cast<UINT>(desc.Height);
src.PlacedFootprint.Footprint.Depth = static_cast<UINT>(desc.DepthOrArraySize);
src.PlacedFootprint.Footprint.RowPitch = static_cast<UINT>(m_RowPitch);
// テクスチャのコピー
pCommandList->CopyTextureRegion(
&dest,
0, // 左上のx座標
0, // 左上のy座標
0, // 左上のz座標
&src,
nullptr // 3Dボックスの設定
);
// リソースの状態を遷移させる
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = m_pTexture;
// コピー先
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
// ピクセルシェーダー
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
pCommandList->ResourceBarrier(1, &barrier);
m_Count = 0;
m_State = STATE_COPY_WAIT;
}
コピーの後はテクスチャリソースをピクセルシェーダーで使用できるように状態を遷移させます。
コピーが終わったらアップロードリソースは削除しても問題ないようなので、適当に待たせて消しています。
void Texture::Update(ID3D12GraphicsCommandList* pCommandList)
{
switch (m_State)
{
case STATE_COPY:
CopyTexture(pCommandList);
break;
case STATE_COPY_WAIT:
if (++m_Count > WAIT_COUNT)
{
// コピーが終わったら消してもいい
SAFE_RELEASE(m_pTextureUpload);
m_State = STATE_IDLE;
}
break;
case STATE_IDLE:
break;
default:
;
}
}
シェーダーリソースビュー
シェーダーでテクスチャデータを使用するために必要になります。
CreateShaderResourceViewを使用します。
// シェーダーリソースビューの作成
void Texture::CreateSRV(ID3D12Device* pDevice, D3D12_CPU_DESCRIPTOR_HANDLE handle)
{
if (m_pTexture == nullptr) return;
D3D12_RESOURCE_DESC desc = m_pTexture->GetDesc();
// シェーダーリソースビューの設定
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
// デフォルト
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
// フォーマット
srvDesc.Format = desc.Format;
// 2Dテクスチャ
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
// ミップレベル
srvDesc.Texture2D.MipLevels = static_cast<UINT>(desc.MipLevels);
// シェーダーリソースビューの作成
pDevice->CreateShaderResourceView(m_pTexture, &srvDesc, handle);
}
SRVの作成にはディスクリプタヒープが必要なので、引数でHANDLEをセットできるようにしています。
参考:ディスクリプタヒープ
適宜、下記のようなヒープを作成して、引数でm_pSRVHeap->GetCPUDescriptorHandleForHeapStart()の値を渡します。
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.NumDescriptors = 1;
// 定数バッファーやシェーダーリソース
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
// シェーダーから参照可
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
// ディスクリプタヒープの作成
result = pDevice->CreateDescriptorHeap(
&heapDesc,
IID_PPV_ARGS(&m_pSRVHeap)
);
次はシェーダーやルートシグネチャなどの設定
コメント