作ったポリゴンにテクスチャを貼りたい。
テクスチャは今回のポリゴンだけでなく、FBXファイルにも使うし2Dでも使うので、別クラスとして用意しておこう。
このクラスでやることをざっくりまとめると
画像ファイルを開く
それを使ってテクスチャを作る
後で必要になる雑多なことをやっておく
という感じ。
2と3はDirect3Dの仕事だが、1に関してはWICを使用する。
WICとは「Windows Imaging Component 」の略で、各種画像ファイルを扱うためのWindowsに標準搭載されている機能。
さらに、WICはCOMの一部です。COMとは「Component Object Model 」の略で……説明面倒なのでこちらをどうぞ。⇒ ゼロからのCOM
<Texture.h>
#pragma once
#include <d3d11.h>
#include "string"
class Texture
{
public:
Texture();
~Texture();
HRESULT Load(std::string fileName);
void Release();
};
とりあえずこんな感じ。
cppは自分で書いて。
最初にCOMの初期化を行い、WICを使って画像ファイルを開く。
#include <wincodec.h>
#include "Texture.h"
#pragma comment( lib, "WindowsCodecs.lib" )
:
:
:
HRESULT Texture::Load(std::string fileName)
{
CoInitialize(nullptr);
IWICImagingFactory *pFactory = nullptr;
IWICBitmapDecoder *pDecoder = nullptr;
IWICBitmapFrameDecode* pFrame = nullptr;
IWICFormatConverter* pFormatConverter = nullptr;
CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<void **>(&pFactory));
HRESULT hr = pFactory->CreateDecoderFromFilename(fileName.c_str(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);
pDecoder->GetFrame(0, &pFrame);
pFactory->CreateFormatConverter(&pFormatConverter);
pFormatConverter->Initialize(pFrame, GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, NULL, 1.0f, WICBitmapPaletteTypeMedianCut);
CoUninitialize();
これだとファイル名のところでエラーが出る。
ファイル名をワイド文字で指定しなければならない。
「mbstowcs_s」を使えばマルチバイト→ワイドに変換できる
wchar_t wtext[FILENAME_MAX];
size_t ret;
mbstowcs_s(&ret, wtext, fileName.c_str(), fileName.length());
:
:
:
HRESULT hr = pFactory->CreateDecoderFromFilename(wtext, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);
テクスチャを作るときに必要になるのでサイズを調べておく。
UINT imgWidth;
UINT imgHeight;
pFormatConverter->GetSize(&imgWidth, &imgHeight);
画像ファイルと同じ大きさのテクスチャを作成
ID3D11Texture2D* pTexture;
D3D11_TEXTURE2D_DESC texdec;
texdec.Width = imgWidth;
texdec.Height = imgHeight;
texdec.MipLevels = 1;
texdec.ArraySize = 1;
texdec.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texdec.SampleDesc.Count = 1;
texdec.SampleDesc.Quality = 0;
texdec.Usage = D3D11_USAGE_DYNAMIC;
texdec.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texdec.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
texdec.MiscFlags = 0;
Direct3D::pDevice->CreateTexture2D(&texdec, nullptr, &pTexture);
デバイスコンテキストを使って、テクスチャに画像を転写する。
D3D11_MAPPED_SUBRESOURCE hMappedres;
Direct3D::pContext->Map(pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &hMappedres);
pFormatConverter->CopyPixels(nullptr, imgWidth * 4, imgWidth * imgHeight * 4, (BYTE*)hMappedres.pData);
Direct3D::pContext->Unmap(pTexture, 0);
CopyPixelsの第2引数はストライド(横1列)、第3引数は全体のサイズ。それぞれ1ピクセル4バイトなので4倍している。
テクスチャの貼り方を決める。詳細は後でいじってみるとわかる。
これは描画するときに必要になる情報なのでメンバ変数にしておく。
<Texture.h>
class Texture
{
ID3D11SamplerState* pSampler_;
public:
Texture();
~Texture();
<Texture.cpp>(さっきまでの続き)
D3D11_SAMPLER_DESC SamDesc;
ZeroMemory(&SamDesc, sizeof(D3D11_SAMPLER_DESC));
SamDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
SamDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
SamDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
SamDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
Direct3D::pDevice->CreateSamplerState(&SamDesc, &pSampler_);
テクスチャをシェーダーに渡すためのビューを作成する。
これもメンバ関数にしておく。
<Texture.h>
class Texture
{
ID3D11SamplerState* pSampler_;
ID3D11ShaderResourceView* pSRV_;
public:
Texture();
~Texture();
<Texture.cpp>(さっきまでの続き)
D3D11_SHADER_RESOURCE_VIEW_DESC srv = {};
srv.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srv.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srv.Texture2D.MipLevels = 1;
Direct3D::pDevice->CreateShaderResourceView(pTexture, &srv, &pSRV_);
pTexture->Release();
return S_OK;
}
これが準備できた時点でpTextureは開放してOK。
サンプラーとシェーダーリソースビューは外部から必要になる情報なので、ゲッターを作っておく。
<Texture.h>
class Texture
{
:
:
:
ID3D11SamplerState* GetSampler();
ID3D11ShaderResourceView* GetSRV();
};
実装(中身)はご自分で。
Release関数で、サンプラーとシェーダーリソースビューを開放する。
これでTextureクラス完成。
現在、シェーダーは真っ白な色を出すように作っている。
テクスチャとサンプラーとUVの情報を受け取って、テクスチャの色を表示するよう変更する。
テクスチャとサンプラーはグローバルで、UV情報は頂点毎に受け取る。
//───────────────────────────────────────
// テクスチャ&サンプラーデータのグローバル変数定義
//───────────────────────────────────────
Texture2D g_texture : register(t0); //テクスチャー
SamplerState g_sampler : register(s0); //サンプラー
//───────────────────────────────────────
// コンスタントバッファ
// DirectX 側から送信されてくる、ポリゴン頂点以外の諸情報の定義
//───────────────────────────────────────
cbuffer global
{
float4x4 matWVP; // ワールド・ビュー・プロジェクションの合成行列
};
//───────────────────────────────────────
// 頂点シェーダー出力&ピクセルシェーダー入力データ構造体
//───────────────────────────────────────
struct VS_OUT
{
float4 pos : SV_POSITION; //位置
float2 uv : TEXCOORD; //UV座標
};
//───────────────────────────────────────
// 頂点シェーダ
//───────────────────────────────────────
VS_OUT VS(float4 pos : POSITION, float4 uv : TEXCOORD)
{
//ピクセルシェーダーへ渡す情報
VS_OUT outData;
//ローカル座標に、ワールド・ビュー・プロジェクション行列をかけて
//スクリーン座標に変換し、ピクセルシェーダーへ
outData.pos = mul(pos, matWVP);
outData.uv = uv;
//まとめて出力
return outData;
}
//───────────────────────────────────────
// ピクセルシェーダ
//───────────────────────────────────────
float4 PS(VS_OUT inData) : SV_Target
{
return g_texture.Sample(g_sampler, inData.uv);
}
頂点毎にUV座標を持たせなければならない。
今までは「位置」だけだったので、頂点情報はXMVECTOR型だったが、2つの情報が必要なので構造体にする。
<Quad.h>
#pragma once
#include <DirectXMath.h>
#include "Direct3D.h"
#include "Texture.h"
using namespace DirectX;
//コンスタントバッファー
struct CONSTANT_BUFFER
{
XMMATRIX matWVP;
};
//頂点情報
struct VERTEX
{
XMVECTOR position;
XMVECTOR uv;
};
class Quad
{
<Quad.cpp>
void Quad::Initialize()
{
// 頂点情報
VERTEX vertices[] =
{
{ XMVectorSet(-1.0f, 1.0f, 0.0f, 0.0f),XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f) }, // 四角形の頂点(左上)
{ XMVectorSet(1.0f, 1.0f, 0.0f, 0.0f), XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f) }, // 四角形の頂点(右上)
{ XMVectorSet(1.0f, -1.0f, 0.0f, 0.0f), XMVectorSet(1.0f, 1.0f, 0.0f, 0.0f) }, // 四角形の頂点(右下)
{ XMVectorSet(-1.0f, -1.0f, 0.0f, 0.0f),XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f) }, // 四角形の頂点(左下)
};
void Quad::Draw(XMMATRIX& worldMatrix)
{
:
:
:
//頂点バッファ
UINT stride = sizeof(VERTEX);
UINT offset = 0;
Direct3D::pContext->IASetVertexBuffers(0, 1, &pVertexBuffer_, &stride, &offset);
1頂点に持たせる情報が変わったので、頂点インプットレイアウトを修正。
void Direct3D::InitShader()
{
// 頂点シェーダの作成(コンパイル)
ID3DBlob *pCompileVS = nullptr;
D3DCompileFromFile(L"Simple3D.hlsl", nullptr, nullptr, "VS", "vs_5_0", NULL, 0, &pCompileVS, NULL);
pDevice->CreateVertexShader(pCompileVS->GetBufferPointer(), pCompileVS->GetBufferSize(), NULL, &pVertexShader);
//頂点インプットレイアウト
D3D11_INPUT_ELEMENT_DESC layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, //位置
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, sizeof(XMVECTOR) , D3D11_INPUT_PER_VERTEX_DATA, 0 },//UV座標
};
pDevice->CreateInputLayout(layout, 2, pCompileVS->GetBufferPointer(), pCompileVS->GetBufferSize(), &pVertexLayout);
pCompileVS->Release();
いよいよポリゴンにテクスチャを貼って表示する。
プロジェクトフォルダにAssetsフォルダを作成し、その中に適当な画像ファイルを入れておこう。
<Quad.h>
#pragma once
#include <DirectXMath.h>
#include "Direct3D.h"
#include "Texture.h"
using namespace DirectX;
class Quad
{
ID3D11Buffer *pVertexBuffer_;
ID3D11Buffer *pIndexBuffer_;
ID3D11Buffer *pConstantBuffer_;
Texture* pTexture_;
<Quad.cpp>
void Quad::Initialize()
{
:
:
:
pTexture_ = new Texture;
pTexture_->Load("Assets\\●●.png");
}
void Quad::Draw(XMMATRIX& worldMatrix)
{
//コンスタントバッファに渡す情報
D3D11_MAPPED_SUBRESOURCE pdata;
CONSTANT_BUFFER cb;
cb.matWVP = XMMatrixTranspose(worldMatrix * Camera::GetViewMatrix() * Camera::GetProjectionMatrix());
Direct3D::pContext->Map(pConstantBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &pdata);
memcpy_s(pdata.pData, pdata.RowPitch, (void*)(&cb), sizeof(cb));
ID3D11SamplerState* pSampler = pTexture_->GetSampler();
Direct3D::pContext->PSSetSamplers(0, 1, &pSampler);
ID3D11ShaderResourceView* pSRV = pTexture_->GetSRV();
Direct3D::pContext->PSSetShaderResources(0, 1, &pSRV);
Direct3D::pContext->Unmap(pConstantBuffer_, 0);
void Quad::Release()
{
pTexture_->Release();
SAFE_DELETE(pTexture_);
SAFE_RELEASE(pConstantBuffer_);
SAFE_RELEASE(pIndexBuffer_);
SAFE_RELEASE(pVertexBuffer_);
}