DirectX12 テクスチャマッピング

今回は、テクスチャを貼ってみたいと思います。
テクスチャとして貼る画像はBMPファイルで行います。

新しくクラスを作るのもアレなので、
前回使ったTriangleクラスを使ってやって行きます。

```c++:Triangle.h
#include "Descriptor.h"
#include "Typedef.h"
#include <vector>
#include <string>
class Triangle :
   public Descriptor
{
   // BMPデータ
   struct BMP {
      LONG sizeX;
      LONG sizeY;
      std::vector<UCHAR>data;
      ID3D12Resource* resource;
   };
public:
   // コンストラクタ
   Triangle(Device* dev, List* list);
   // デストラクタ
   ~Triangle();

   // BMP読み込み
   HRESULT LoadBMP(std::string fileName);

   // 描画
   void Draw(void);
private:
   // 定数バッファリソースの生成
   HRESULT CreateConResource(void);
   // シェーダリソースビューの生成
   HRESULT CreateShaderView(void);
   // 頂点リソースの生成
   HRESULT CreateResource(void);

   // 頂点
   std::vector<Vertex>vertex;
   // リソース
   ID3D12Resource * resource;
   // 送信データ
   UINT8* data;
   // 頂点バッファビュー
   D3D12_VERTEX_BUFFER_VIEW view;
   // BMP
   BMP bmp;
};
```

```c++:Triangle.cpp
#include "Triangle.h"
#include "Device.h"
#include "List.h"

// 頂点の最大数
#define VERTEX_MAX 4

// コンストラクタ
Triangle::Triangle(Device* dev, List* list) :
   resource(nullptr), data(nullptr)
{
   this->dev = dev;
   this->list = list;

   vertex.clear();
   view = {};
   bmp = {};
}
// デストラクタ
Triangle::~Triangle()
{
   Release(resource);
   Release(bmp.resource);
   Release(heap);
   delete list;
   delete dev;
}
// 定数バッファリソースの生成
HRESULT Triangle::CreateConResource(void)
{
   result = CreateHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);

   //プロパティ設定用構造体の設定
   D3D12_HEAP_PROPERTIES prop = {};
   prop.CPUPageProperty      = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
   prop.CreationNodeMask     = 1;
   prop.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
   prop.Type                 = D3D12_HEAP_TYPE_CUSTOM;
   prop.VisibleNodeMask      = 1;

   //リソース設定用構造体の設定
   D3D12_RESOURCE_DESC desc = {};
   desc.Dimension        = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
   desc.Width            = bmp.sizeX;
   desc.Height           = bmp.sizeY;
   desc.DepthOrArraySize = 1;
   desc.Format           = DXGI_FORMAT_R8G8B8A8_UNORM;
   desc.SampleDesc.Count = 1;
   desc.Flags            = D3D12_RESOURCE_FLAG_NONE;
   desc.Layout           = D3D12_TEXTURE_LAYOUT_UNKNOWN;

   //リソース生成
   result = dev->Get()->CreateCommittedResource(&prop, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bmp.resource));

   return result;
}
// シェーダリソースビューの生成
HRESULT Triangle::CreateShaderView(void)
{
   //シェーダリソースビュー設定用構造体の設定
   D3D12_SHADER_RESOURCE_VIEW_DESC desc = {};
   desc.Format                    = DXGI_FORMAT_R8G8B8A8_UNORM;
   desc.ViewDimension             = D3D12_SRV_DIMENSION_TEXTURE2D;
   desc.Texture2D.MipLevels       = 1;
   desc.Texture2D.MostDetailedMip = 0;
   desc.Shader4ComponentMapping   = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;

   //ヒープの先頭ハンドルを取得
   D3D12_CPU_DESCRIPTOR_HANDLE handle = heap->GetCPUDescriptorHandleForHeapStart();

   //シェーダーリソースビューの生成
   dev->Get()->CreateShaderResourceView(data[index].resource, &shaderDesc, handle);

   return result;
}
// 頂点リソースの生成
HRESULT Triangle::CreateResource(void)
{
   //頂点の数分メモリを確保
   vertex.resize(VERTEX_MAX);
   vertex[0] = { { -1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f } };//左上
   vertex[1] = { {  1.0f, -1.0f, 0.0f }, { 1.0f, 0.0f } };//右上
   vertex[2] = { { -1.0f,  1.0f, 0.0f }, { 0.0f, 1.0f } };//左下
   vertex[3] = { {  1.0f,  1.0f, 0.0f }, { 1.0f, 1.0f } };//右下

   //プロパティ設定用構造体の設定
   D3D12_HEAP_PROPERTIES prop = {};
   prop.Type                 = D3D12_HEAP_TYPE_UPLOAD;
   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.Width            = sizeof(Vertex) * vertex.size();
   desc.Height           = 1;
   desc.DepthOrArraySize = 1;
   desc.MipLevels        = 1;
   desc.Format           = DXGI_FORMAT_UNKNOWN;
   desc.SampleDesc.Count = 1;
   desc.Flags            = D3D12_RESOURCE_FLAG_NONE;
   desc.Layout           = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

   //リソース生成
   result = dev->Get()->CreateCommittedResource(&prop, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&resource));

   //送信範囲
   D3D12_RANGE range = { 0,0 };

   //マッピング
   result = resource->Map(0, &range, reinterpret_cast<void**>(&data));

   //頂点データのコピー
   memcpy(data, vertex.data(), sizeof(Vertex) * vertex.size());

   //アンマップ
   resource->Unmap(0, nullptr);

   //頂点バッファ設定用構造体の設定
   view.BufferLocation = resource->GetGPUVirtualAddress();
   view.SizeInBytes    = sizeof(Vertex) * vertex.size();
   view.StrideInBytes  = sizeof(Vertex);

   return result;
}
// BMP読み込み
HRESULT Triangle::LoadBMP(std::string fileName)
{
   //BMPヘッダー構造体
   BITMAPINFOHEADER header = {};
   //BMPファイルヘッダー
   BITMAPFILEHEADER fileHeader = {};
   //ダミー宣言
   const char* path = fileName.c_str();

   //ファイル
   FILE *file;

   //ファイル開らく
   if ((fopen_s(&file, path, "rb")) != 0)
   {
      return S_FALSE;
   }

   //BMPファイルヘッダー読み込み
   fread(&fileheader, sizeof(fileheader), 1, file);

   //BMPヘッダー読み込み
   fread(&header, sizeof(header), 1, file);

   //画像の幅と高さの保存
   bmp.sizeX = header.biWidth;
   bmp.sizeY = header.biHeight;

   //データサイズ分のメモリ確保(ビットの深さが32bitの場合)
   //bmp.resize(header.biSizeImage);
   //データサイズ分のメモリ確保(ビットの深さが24bitの場合)
   bmp.data.resize(header.biWidth * header.biHeight * 4);

   for (int line = header.biHeight - 1; line >= 0; --line)
   {
      for (int count = 0; count < header.biWidth * 4; count += 4)
      {
         //一番左の配列番号
         UINT address = line * header.biWidth * 4;
         bmp.data[address + count] = 0;
         fread(&bmp.data[address + count + 1], sizeof(UCHAR), 3, file);
      }
   }

   //ファイルを閉じる
   fclose(file);

   result = CreateConResource();
   result = CreateShaderView();
   result = CreateResource();

   return result;
}
// 描画
void Triangle::Draw(void)
{
   //ボックス設定用構造体の設定
   D3D12_BOX box = {};
   box.back   = 1;
   box.bottom = bmp.sizeY;
   box.front  = 0;
   box.left   = 0;
   box.right  = bmp.sizeX;
   box.top    = 0;

   //サブリソースに書き込み
   result = bmp.resource->WriteToSubresource(0, &box, &bmp.data[0], (box.right * 4), (box.bottom * 4));

   //ヒープの先頭ハンドルを取得
   D3D12_GPU_DESCRIPTOR_HANDLE handle = heap->GetGPUDescriptorHandleForHeapStart();

   //ヒープのセット
   list->SetDescriptorHeaps(1, &heap);

   //ディスクラプターテーブルのセット
   list->SetGraphicsRootDescriptorTable(1, handle);

   //頂点バッファビューのセット
   list->GetList()->IASetVertexBuffers(0, 1, &view);

   //トポロジータイプのセット
   list->GetList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

   //描画
   list->GetList()->DrawInstanced(VERTEX_MAX, 1, 0, 0);
}
```

デフォルトで24bit深度のBMP画像を読み込むようにしてます。
続いてシェーダ側です。

```c++:Shader.hlsl
Texture2D<float4> tex : register(t0);
SamplerState      smp : register(s0);

//ワールドビュープロジェクション
cbuffer wvp : register(b0)
{
   //ワールド行列
   float4x4 world;
   //ビュープロジェクション行列
   float4x4 viewProjection;
}
//出力
struct Out
{
   //システム座標
   float4 svpos  : SV_POSITION;
   //座標
   float4 pos    : POSITION;
   //uv値
   float2 uv     : TEXCOORD;
};
//入力
struct Input
{
   //座標
   float4 pos : POSITION;
   //uv
   float2 uv  : TEXCOORD;
};

// 頂点シェーダ
Out VS(Input input)
{
   input.pos = mul(mul(viewProjection, world), input.pos);
   Out o;
   o.svpos = input.pos;
   o.pos   = input.pos;
   o.uv    = input.uv;

   return o;
}

// ピクセルシェーダ
float4 PS(Out o) : SV_TARGET
{
   return tex.Sample(smp, o.uv);
}
```

これで、Triangleクラスをnewした後に、LoadBMP関数を呼んであげれば完成です。