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

テクスチャの表示 DirextX

DirectXTexの準備ができたら、画像を読み込んでデータを取得し、描画に必要なリソースを作成していきます。

P1:DirectXTexの準備
P2:画像の読み込み、リソースの作成とコピー
  シェーダーリソースビューの作成

P3:各所の設定
P4:テクスチャ表示

スポンサーリンク

読み込みとリソース作成

テクスチャ用のクラスを用意するので、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で画像ファイルを読み込みます。

問題なくロードが完了すると、TexMetadataScratchImageに画像情報がセットされます。

// テクスチャーの作成
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;
}

CoInitializeExを呼ばないとエラーになることがあるようです

 

リソースを作成

テクスチャ情報のリソース作成はCreateCommittedResourceを使用します。

D3D12_HEAP_PROPERTIESD3D12_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)
);

 

次はシェーダーやルートシグネチャなどの設定

かれいど

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

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

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

コメント