作ったポリゴンにテクスチャを貼りたい。
テクスチャは今回のポリゴンだけでなく、FBXファイルにも使うし2Dでも使うので、別クラスとして用意しておこう。
このクラスでやることをざっくりまとめると
画像ファイルを開く
それを使ってテクスチャを作る
後で必要になる雑多なことをやっておく
という感じ。
画像の読み込みには外部ライブラリを導入しそれを使う。WindowsのAPIのみを使って画像を読み込んだりもできるが(本当はこっちの方法も覚えていると就職したときに役に立つことも多い)、今回は画像を簡単に自分のプログラム内に読み込めるDirectXTexというMSの作ったライブラリを使ってゆく。DirectXTexを利用することで様々な形式をDirectX11で使えるShaderResourceView(SRV)に簡単に変換することができる。
DirectXTexの導入ははgithubから最新の物を直接持ってくるかNuGetを使う方法がある。
NuGetを使った方が少し融通は利かないが簡単なので今回はNuGetで追加してゆく。
(githubから持ってきたい方はリポジトリはこちらから)
ちなみにNugetは、VisualStudioに搭載されているオープンソースのライブラリやパッケージ(追加機能やソフト)の管理アプリケーションである。
導入はプロジェクトごとになるので注意すること。
まずメニューから、「ツール → NuGetパッケージマネージャー → ソリューションのNugetパッケージの管理」を選択
パッケージマネージャの画面が表示されるはず。
「参照」をクリックし、検索ウィンドウに「DirectXTex」と入力する。
Nugetの管理下にある、インストール可能なDirectXTexのバージョンがいくつか表示される。
表示された、インストール可能バージョンの中から、Win10対応のものを選択する。
右側の詳細設定が表示されるので、適用するプロジェクトにチェックを入れてから、インストールをクリックする(1個下の画像を参照)
<Texture.h>
#pragma once
#include <d3d11.h>
#include "string"
class Texture
{
public:
Texture();
~Texture();
HRESULT Load(std::string fileName);
void Release();
};
とりあえずこんな感じ。
cppは自分で書いて。
ライブラリのリンク設定を書いておいて、Load関数でお手軽にファイルを読み込む。
画像ファイル名を指定すると、内部でテクスチャ形式のものが出来上がるので、そのままShaderResourceViewを作成可能である。
#include <wincodec.h>
#include "Texture.h"
#include <DirectXTex.h>
// DirectXTexのライブラリをリンク
#pragma comment(lib, "DirectXTex.lib")
:
:
:
HRESULT Texture::Load(LPCWSTR fileName)
{
TexMetadata metadata; //画像の付属情報
ScratchImage image; //画像本体
HRESULT hr;
//実際に読んでゆくぅ
hr = LoadFromWICFile(fileName, WIC_FLAGS::WIC_FLAGS_NONE,
&metadata, image);
if (FAILED(hr))
{
return S_FALSE;
}
これだとファイル名のところでエラーが出る。
テクスチャの貼り方を決める。詳細は後でいじってみるとわかる。
これは描画するときに必要になる情報なのでメンバ変数にしておく。
<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;
hr = CreateShaderResourceView(Direct3D::pDevice,
image.GetImages(),image.GetImageCount(),metadata, &pSRV_);
}
これが準備できた時点で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_);
}