22.テクスチャの作成

前回途中だった、「配置オブジェクトの初期化処理」の続きです。
今回は「テクスチャ」についてです。

Dx12に関する情報は、マイクロソフト社の「DirectX-Graphics-Samples」が主な情報源となります。

マイクロソフト社の「DirectX-Graphics-Samples」のGitHubサイトは以下になります。

https://github.com/Microsoft/DirectX-Graphics-Samples

ぜひ、興味ある方はダウンロードしてみてDx12研究を始めるといいと思います。

この記事は、
コミット「Dx11、Dx12両頂点定義とプリミティブメッシュ作成の追加」
から、
コミット「weak_ptrからshared_ptr取得方法の修正」
の間の作業です。
GitHubサイト

https://github.com/WiZFramework/BaseCross

を参照して下さい。
その間にいくつかのコミットがありますが、後ろのコミットを開いていただいてそれと合わせ読んでいただくとわかりやすいと思います。

オブジェクトの初期化処理
配置されるオブジェクトは、「BaseCrossDx12」プロジェクトの「Character.h/cpp」に記述があります。「NormalTextureBoxクラス」です。
このオブジェクトの初期化処理は、「NormalTextureBox::OnCreate関数」で、以下がその実体です。

void NormalTextureBox::OnCreate() {
    m_CubeMesh = MeshResource::CreateCube(1.0f);
    wstring DataDir;
    App::GetApp()->GetDataDirectory(DataDir);
    wstring strTexture = DataDir + L"wall.jpg";
    m_WallTex = TextureResource::CreateTextureResource(strTexture);
    m_DrawContext = ObjectFactory::Create<VSPSDrawContext>(VSPSDrawContext::CreateParam::CreateSrvSmpCbv);
    m_DrawContext->CreateConstantBuffer(sizeof(m_ConstantBufferData));
    m_DrawContext->CreateDefault3DPipelineCmdList<VertexPositionNormalTexture, VSPNTStatic, PSPNTStatic>();
    ZeroMemory(&m_ConstantBufferData, sizeof(m_ConstantBufferData));
    //各行列をIdentityに初期化
    m_ConstantBufferData.World = Matrix4X4EX::Identity();
    m_ConstantBufferData.View = Matrix4X4EX::Identity();
    m_ConstantBufferData.Projection = Matrix4X4EX::Identity();
    //初期値更新
    m_DrawContext->UpdateConstantBuffer(reinterpret_cast<void**>(&m_ConstantBufferData), sizeof(m_ConstantBufferData));
    //テクスチャ設定
    m_DrawContext->SetTextureResource(m_WallTex);
}

今回は以下のブロックの説明になります。

    wstring DataDir;
    App::GetApp()->GetDataDirectory(DataDir);
    wstring strTexture = DataDir + L"wall.jpg";
    m_WallTex = TextureResource::CreateTextureResource(strTexture);
);
    //中略

    //テクスチャ設定
    m_DrawContext->SetTextureResource(m_WallTex);
}

テクスチャとは何か
「テクスチャ」というと、画像ファイルそのものと考える人もいるかもしれませんが、本来は、「模様リソース」のような意味になります。つまり「画像ファイルがなくてもテクスチャは作成できる」ということです。
BaseCrossには「DirectXTex」というマイクロソフト社のライブラリを「プロジェクト」として実装しています。
これは、様々な画像ファイルを「テクスチャ」にしてくれるライブラリで、様々なフォーマットの「画像ファイル」を一括管理できるので大変重宝します。GitHub公開されていますので、修正やバージョンアップした場合も最新のものを導入可能です。

マイクロソフト社の「DirectXTex」のGitHubサイトは以下になります。

https://github.com/Microsoft/DirectXTex

ぜひ、興味ある方はダウンロードしてみるといいと思います。

 

BaseCrossにおけるテクスチャの扱い
BaseCrossにおいてテクスチャは「テクスチャリソース」というクラスで管理します。
「テクスチャリソース」クラスは「TextureResourceクラス」です「Dx12Lib」プロジェクトの「DeviceResources.h/cpp」に記述があります。
以下がその宣言の抜粋です。

//--------------------------------------------------------------------------------------
/// メモリ上に保持するDx12テクスチャリソース
//--------------------------------------------------------------------------------------
class TextureResource : public BaseResource {
    //中略
public:
    //中略

    //--------------------------------------------------------------------------------------
    /*!
    @brief メモリ上にテクスチャリソースの作成
    @param[in]  Width 幅
    @param[in]  Height 高さ
    @return テクスチャリソースのshared_ptr
    */
    //--------------------------------------------------------------------------------------
    static  shared_ptr<TextureResource> CreateTextureResource(uint32_t Width, uint32_t Height, const vector<uint8_t>& data);
    //--------------------------------------------------------------------------------------
    /*!
    @brief 画像ファイルからテクスチャリソースの作成
    @param[in]  FileName ファイル名
    @param[in]  TexType 画像タイプ
    @return テクスチャリソースのshared_ptr
    */
    //--------------------------------------------------------------------------------------
    static  shared_ptr<TextureResource> CreateTextureResource(const wstring& FileName, const wstring& TexType = L"WIC");

    //中略
private:
    // pImplイディオム
    struct Impl;
    unique_ptr<Impl> pImpl;
};

このように「Implイディオム」を持つクラスで、「BaseResource」の派生クラスとして管理されます。
「TextureResourceクラス」はコンストラクタは公開されてないので、テクスチャを構築するにはstatic関数を使って

    wstring DataDir;
    App::GetApp()->GetDataDirectory(DataDir);
    wstring strTexture = DataDir + L"wall.jpg";
    m_WallTex = TextureResource::CreateTextureResource(strTexture);

のように初期化します。上記はデータディレクトリ(mediaディレクトリ)にある、「wall.jpg」を「画像ファイル名」を渡すことで初期化しています。
そのほかに、「テクスチャの幅と高さとデータそのもの」を渡す初期化もあります。

    static  shared_ptr<TextureResource> CreateTextureResource(uint32_t Width, uint32_t Height, const vector<uint8_t>& data);

を使う方法ですね。
こちらの方法は、この先、直接データからテクスチャを構築するサンプルを用意する予定なので、今回は説明は省きます。画像ファイルから構築する方法を考えます。

static関数である「TextureResource::CreateTextureResource関数」を呼ぶと、この関数からは「TextureResource::OnCreateWithParam関数」が呼ばれるわけですが、その内容の抜粋です

void TextureResource::OnCreateWithParam(const wstring& FileName, const wstring& TexType) {
    //ファイルでの構築
    try {

        //中略

        //テクスチャ作成
        DirectX::TexMetadata metadata;
        DirectX::ScratchImage image;

        //中略
        //この中で、DirectXTexの関数により、metadataとimageに、データが読み込まれる

        pImpl->CreateTexture(image.GetImages(), image.GetImageCount(), metadata);
        pImpl->CreateTextureUploadHeap();

    }
    catch (...) {
        throw;
    }
}

中略の部分は、ファイルが存在するかとか、ファイル形式はどの形式かなどを検証して、DirectTexの関数により「metadata」「image」にデータが読み込まれます。
「TexMetadata」にはこのテクスチャの基本情報が入ります。基本情報には、幅とか高さなどの情報も含まれます。
「image」はデータそのものです。「image.GetImages()」によりデータのポインタを取得でき、「image.GetImageCount()」によりそのデータのサイズを取得できます。
これらの情報をもとに、「pImpl->CreateTexture関数」を呼び出します。
「pImpl->CreateTexture関数」は結構いろんな処理をしているのですが、一番の目的は「struct TextureResource::Impl」にある

//テクスチャリソース
ComPtr<ID3D12Resource> m_Texture;
//テクスチャデータ
vector<uint8_t> m_Data;

のメンバ変数を初期化することです。これを作成するために「D3D12_RESOURCE_DESC m_TextureResDesc」メンバを設定し、

ThrowIfFailed(
    Dev->GetDevice()->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE, &m_TextureResDesc,
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_PPV_ARGS(&m_Texture)),
    L"テクスチャリソースの作成に失敗しました",
    L"Dev->GetDevice()->CreateCommittedResource()",
    L"TextureResource::Impl::CreateTexture()"
);

と「Dev->GetDevice()->CreateCommittedResource関数」を呼び出します。これによりテクスチャリソース(ComPtr<ID3D12Resource>型)が生成されます。
その後、m_Dataを、イメージに合わせ初期化します。これはデータそのものです。

「TextureResource::OnCreateWithParam関数」では、その後、

    pImpl->CreateTextureUploadHeap();

を実行します。この関数は、前項「コンスタントバッファ」のところでもやったように、テクスチャリソースをGPUにアップロードすためのヒープ領域の確保です。

ここまでの処理で、なんとなく見えてきたと思います。
まず、描画するためにシェーダに渡すリソースは自分で確保しなければならないこと。これは「コンスタントバッファ」の場合はcpp側構造体にインスタンスとして、そして「テクスチャ」の場合は、「ComPtr<ID3D12Resource>」および「データそのもの」で実装しました。
次に「アップロードヒープ」というのが必要なのがわかりました。これは「コンスタントバッファ」も「テクスチャ」も同様です。
そしてコンスタントバッファでは「コンスタントバッファビュー」として作成した「ビュー」が「テクスチャ」にも必要なことがわかります。
コンスタントバッファは「VSPSDrawContext」のメンバ関数で作成してました。では、テクスチャの場合はどこで作成しているのでしょうか?
それが「NormalTextureBox::OnCreate関数」で呼ばれる

    //テクスチャ設定
    m_DrawContext->SetTextureResource(m_WallTex);

で行ってます。以下のような実装になってます。

void VSPSDrawContext::SetTextureResource(const shared_ptr<TextureResource>& textureResorce) {
    pImpl->CbvSrvUavDescriptorHeapChk();
    pImpl->m_TextureResource = textureResorce;
    //デバイスの取得
    auto Dev = App::GetApp()->GetDeviceResources();
    //ハンドルを作成
    CD3DX12_CPU_DESCRIPTOR_HANDLE Handle(
        pImpl->m_CbvSrvUavDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),
        1,
        0
    );
    //テクスチャのシェーダリソースビューを作成
    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    //フォーマット
    srvDesc.Format = textureResorce->GetTextureResDesc().Format;
    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MipLevels = textureResorce->GetTextureResDesc().MipLevels;
    Dev->GetDevice()->CreateShaderResourceView(
        textureResorce->GetTexture().Get(),
        &srvDesc,
        Handle);
}

このように、「テクスチャ」も「ビュー」を作成しています。コンスタントバッファのビューは「コンスタントバッファビュー」と言ってましたが、テクスチャのビューは「シェーダリソースビュー」という名称となります。

さて、ここまで、前項では「コンスタントバッファ」今項では「テクスチャ」の実装を行いました。それぞれ必要な処理と行っている個所を以下の表で説明します。

2016081501

このように分散されているわけですが、これは、例えば、テクスチャの内容が変わった場合とか、あるいは、このオブジェクトに張り付けられるテクスチャが変わった場合とか、動的な変化も加味してこのようにしてますが、もう少しうまくまとめられるかなとも感じてます。ですので将来仕様が変わる可能性も十分あります。

では次回はいよいよ「コマンドリスト」です。

カテゴリー

ピックアップ記事

  1. 2016092201
    今回は前回のサンプルを少し機能を追加しまして、いろんなオブジェクトを追加しています。FullTuto…
  2. 2016092001
    前回更新から時間がたってしまいましたが、今回はフルバージョンチュートリアル003をアップしました。内…
  3. eyecatch
    前回更新から時間がたってしまいましたが、今回はフルバージョンチュートリアル002で懸案となっていまし…
PAGE TOP