引き続き2Dテクスチャの対応です。
今回は描画範囲の指定、カラーの変更を実装していきます!
定数バッファーの修正
頂点シェーダー
定数バッファーに描画範囲(rect)とカラー値(color)を追加して、VSMainの引数からcolorを消します。
rect.xyはuvの開始位置、rect.zwはuvの幅高さです。
struct VSOutput
{
float4 pos : SV_POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
// 定数バッファー
cbuffer myBuffer : register(b0)
{
matrix mat;
float4 rect;
float4 color;
};
VSOutput VSMain( float4 pos : POSITION, float2 uv : TEXCOORD )
{
VSOutput output;
output.color = color;
output.pos = mul(pos, mat);
output.uv.x = rect.x + uv.x * rect.z;
output.uv.y = rect.y + uv.y * rect.w;
return output;
}
入力アセンブラー
頂点シェーダーでcolor値を消したので、頂点レイアウトを修正します。
// 入力アセンブラー:頂点レイアウト
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{
"POSITION", // セマンティック名
0,
DXGI_FORMAT_R32G32B32_FLOAT, // フォーマット
0,
0,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, // 頂点データ
0
},
{
"TEXCOORD", // セマンティック名
0,
DXGI_FORMAT_R32G32_FLOAT, // フォーマット
0,
12,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, // 頂点データ
0
},
};
表示クラスの修正
rectとcolorを追加して、設定できるように修正しています。
ヘッダーファイル
#pragma once
#include "Texture.h"
// 画像表示のクラス
class Sprite
{
public:
Sprite();
~Sprite();
// 初期化
bool Initialize(ID3D12Device* pDevice, const wchar_t* pFile);
// 更新
void Update(ID3D12GraphicsCommandList* pCommandList);
// 描画
void Draw(ID3D12GraphicsCommandList* pCommandList);
// 各種設定
void SetPosition(int x, int y);
void SetScale(float x, float y);
void SetRotateZ(float degrees);
// 描画範囲を設定
void SetRect(const RECT& rRect);
void SetRect(float x, float y, float w, float h);
// 色変更
void SetColor(UINT8 r, UINT8 g, UINT8 b, UINT8 a);
void SetRed(UINT8 r);
void SetGreen(UINT8 g);
void SetBlue(UINT8 b);
void SetAlpha(UINT8 a);
private:
// バッファーの作成
bool CreateBuffer(ID3D12Device* pDevice);
// マップ
bool Map();
// バッファービューの設定
void SetBufferView();
// 定数バッファービューの設定
void CreateConstantBufferView(ID3D12Device* pDevice);
// ディスクリプタヒープの作成
bool CreateDescriptorHeap(ID3D12Device* pDevice);
// 行列の更新
void UpdateMatrix();
private:
// 頂点情報
struct Vertex
{
XMFLOAT3 Pos;
XMFLOAT2 Uv;
};
Vertex m_Vertices[4];
unsigned short m_IndexData[6];
ID3D12Resource* m_pVertexBuffer;
ID3D12Resource* m_pIndexBuffer;
ID3D12Resource* m_pConstantBuffer;
D3D12_VERTEX_BUFFER_VIEW m_VertexBufferView;
D3D12_INDEX_BUFFER_VIEW m_IndexBufferView;
ID3D12DescriptorHeap* m_pDescHeap;
UINT m_HandleIncrementSize;
Texture* m_pTexture;
XMMATRIX* m_pMatrix;
XMFLOAT4* m_pRect;
XMFLOAT4* m_pColor;
XMINT2 m_Position;
XMFLOAT2 m_Scale;
float m_RotateZ;
XMFLOAT4 m_Rect;
XMFLOAT4 m_Color;
};
C++ファイル
#include "Sprite.h"
#include "Texture.h"
// コンストラクタ
Sprite::Sprite()
: m_IndexData{ 0,1,2,0,2,3 }
, m_pVertexBuffer(nullptr)
, m_pIndexBuffer(nullptr)
, m_pConstantBuffer(nullptr)
, m_VertexBufferView{}
, m_IndexBufferView{}
, m_pDescHeap(nullptr)
, m_pTexture(nullptr)
, m_HandleIncrementSize(0)
, m_pMatrix(nullptr)
, m_pRect(nullptr)
, m_pColor(nullptr)
, m_Position{ 0,0 }
, m_Scale(1.0f, 1.0f)
, m_RotateZ(0.f)
, m_Rect{ 0.0f, 0.0f, 1.0f, 1.0f }
, m_Color{ 1.0f, 1.0f, 1.0f, 1.0f }
{
// 頂点情報
m_Vertices[0].Pos = XMFLOAT3(-1.0f, 1.0f, 0.0f);
m_Vertices[1].Pos = XMFLOAT3(1.0f, 1.0f, 0.0f);
m_Vertices[2].Pos = XMFLOAT3(1.0f, -1.0f, 0.0f);
m_Vertices[3].Pos = XMFLOAT3(-1.0f, -1.0f, 0.0f);
m_Vertices[0].Uv = XMFLOAT2(0.0f, 0.0f);
m_Vertices[1].Uv = XMFLOAT2(1.0f, 0.0f);
m_Vertices[2].Uv = XMFLOAT2(1.0f, 1.0f);
m_Vertices[3].Uv = XMFLOAT2(0.0f, 1.0f);
}
// デストラクタ
Sprite::~Sprite()
{
SAFE_DELETE(m_pTexture);
SAFE_RELEASE(m_pConstantBuffer);
SAFE_RELEASE(m_pIndexBuffer);
SAFE_RELEASE(m_pVertexBuffer);
SAFE_RELEASE(m_pDescHeap);
}
// 初期化
bool Sprite::Initialize(ID3D12Device* pDevice, const wchar_t* pFile)
{
// ディスクリプタヒープの作成
CreateDescriptorHeap(pDevice);
// テクスチャ(リソース)の作成
m_pTexture = new Texture();
if (m_pTexture->CreateTexture(pDevice, pFile) == false) {
return false;
}
// SRVを作成
m_pTexture->CreateSRV(pDevice, m_pDescHeap->GetCPUDescriptorHandleForHeapStart());
// 頂点位置をテクスチャサイズに修正
float width = static_cast<float>(m_pTexture->GetWidth());
float height = static_cast<float>(m_pTexture->GetHeight());
m_Vertices[0].Pos = XMFLOAT3(-width, height, 0.0f);
m_Vertices[1].Pos = XMFLOAT3(width, height, 0.0f);
m_Vertices[2].Pos = XMFLOAT3(width, -height, 0.0f);
m_Vertices[3].Pos = XMFLOAT3(-width, -height, 0.0f);
if (CreateBuffer(pDevice) == false) {
return false;
}
if (Map() == false) {
return false;
}
SetBufferView();
CreateConstantBufferView(pDevice);
return true;
}
// 更新
void Sprite::Update(ID3D12GraphicsCommandList* pCommandList)
{
if (m_pTexture) {
m_pTexture->Update(pCommandList);
}
UpdateMatrix();
}
// 描画
void Sprite::Draw(ID3D12GraphicsCommandList* pCommandList)
{
if (pCommandList == nullptr || m_pTexture == nullptr) return;
if (!m_pTexture->IsCopied()) return;
// ディスクリプタヒープの設定
ID3D12DescriptorHeap* ppHeaps[] = { m_pDescHeap };
pCommandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
D3D12_GPU_DESCRIPTOR_HANDLE handle = m_pDescHeap->GetGPUDescriptorHandleForHeapStart();
// t0に設定
pCommandList->SetGraphicsRootDescriptorTable(0, handle);
// b0に設定
handle.ptr += m_HandleIncrementSize;
pCommandList->SetGraphicsRootDescriptorTable(1, handle);
// 3点で1面の情報
pCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 頂点バッファービューの設定
pCommandList->IASetVertexBuffers(
0, // インデックス
1, // ビューの数
&m_VertexBufferView
);
// インデックスバッファーの設定
pCommandList->IASetIndexBuffer(&m_IndexBufferView);
// インデックスで描画
pCommandList->DrawIndexedInstanced(
_countof(m_IndexData), // インデックスの数
1, // 描画するインスタンスの数
0, // 最初のインデックス
0, // 各インデックスに追加する頂点の値
0 // 一旦0
);
}
// リソースの作成
bool Sprite::CreateBuffer(ID3D12Device* pDevice)
{
if (pDevice == nullptr) return false;
const UINT64 vertexBufferSize = sizeof(m_Vertices);
// 頂点バッファーを作成
HRESULT result = CreateDataResource(pDevice, &m_pVertexBuffer, vertexBufferSize, false);
IS_S_FALSE(result);
// インデックスバッファーを作成
result = CreateDataResource(pDevice, &m_pIndexBuffer, sizeof(m_IndexData), false);
IS_S_FALSE(result);
// 定数バッファーを作成
result = CreateDataResource(pDevice, &m_pConstantBuffer, sizeof(XMMATRIX), true);
IS_S_FALSE(result);
return true;
}
// 頂点バッファービューの設定
void Sprite::SetBufferView()
{
// バッファーの仮想アドレス
m_VertexBufferView.BufferLocation = m_pVertexBuffer->GetGPUVirtualAddress();
// 1頂点のバイトサイズ
m_VertexBufferView.StrideInBytes = sizeof(m_Vertices[0]);
// バッファーのバイトサイズ
m_VertexBufferView.SizeInBytes = sizeof(m_Vertices);
// インデックス
// バッファーの仮想アドレス
m_IndexBufferView.BufferLocation = m_pIndexBuffer->GetGPUVirtualAddress();
// バッファーのフォーマット
m_IndexBufferView.Format = DXGI_FORMAT_R16_UINT;
// バッファーのバイトサイズ
m_IndexBufferView.SizeInBytes = sizeof(m_IndexData);
}
// 定数バッファービューの作成
void Sprite::CreateConstantBufferView(ID3D12Device* pDevice)
{
m_HandleIncrementSize = pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
// SRV分をズラす
D3D12_CPU_DESCRIPTOR_HANDLE handle = m_pDescHeap->GetCPUDescriptorHandleForHeapStart();
handle.ptr += m_HandleIncrementSize;
// CBVを作成
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = m_pConstantBuffer->GetGPUVirtualAddress();
cbvDesc.SizeInBytes = static_cast<UINT>(m_pConstantBuffer->GetDesc().Width);
pDevice->CreateConstantBufferView(&cbvDesc, handle);
}
// ディスクリプタヒープの作成
bool Sprite::CreateDescriptorHeap(ID3D12Device* pDevice)
{
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
// 2つ
heapDesc.NumDescriptors = 2;
// SRVとCBV用の
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
// シェーダーから見える
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
// ディスクリプタヒープを作成
HRESULT result = pDevice->CreateDescriptorHeap(
&heapDesc,
IID_PPV_ARGS(&m_pDescHeap)
);
if (result == S_FALSE) return false;
return true;
}
// 各種設定
void Sprite::SetPosition(int x, int y)
{
m_Position.x = x;
m_Position.y = y;
}
void Sprite::SetScale(float x, float y)
{
m_Scale.x = x;
m_Scale.y = y;
}
void Sprite::SetRotateZ(float degrees)
{
// ラジアンに変換しておく
m_RotateZ = XMConvertToRadians(degrees);
}
Map
定数バッファーに値を増やしたので、値をセットしないといけません。
今回はセット用のポインタを増やして対応しました。ポインタ位置をズラして保持しておきます。
このセット用ポインタを更新すれば、頂点シェーダー側も更新されます。
// マップ
bool Sprite::Map()
{
UINT bufferSize = sizeof(m_Vertices);
// リソースデータのポインターを取得
UINT8* pDataBegin;
HRESULT result = m_pVertexBuffer->Map(
0, // インデックス番号
nullptr, // リソース全体
reinterpret_cast<void**>(&pDataBegin)
);
IS_S_FALSE(result);
// バッファーに情報をコピー
memcpy(pDataBegin, m_Vertices, bufferSize);
// 取得したポインターを無効にする
m_pVertexBuffer->Unmap(0, nullptr);
// インデックス
bufferSize = sizeof(m_IndexData);
result = m_pIndexBuffer->Map(
0, // インデックス番号
nullptr, // リソース全体
reinterpret_cast<void**>(&pDataBegin)
);
IS_S_FALSE(result);
// バッファーに情報をコピー
memcpy(pDataBegin, m_IndexData, bufferSize);
// 取得したポインターを無効にする
m_pIndexBuffer->Unmap(0, nullptr);
// コンスタント
result = m_pConstantBuffer->Map(
0,
nullptr,
reinterpret_cast<void**>(&m_pMatrix)
);
*m_pMatrix = XMMatrixIdentity();
// 行列分ズラす
m_pRect = reinterpret_cast<XMFLOAT4*>(m_pMatrix + 1);
*m_pRect = m_Rect;
// さらにRECT分ズラす
m_pColor = m_pRect + 1;
*m_pColor = m_Color;
return true;
}
行列の計算
描画範囲で設定した部分を左上の位置に表示させたいので、位置とスケールの修正が必要になります。
例:描画範囲(0.5, 0, 0.5, 1.0)、表示位置(0, 0)の場合
// 行列の更新
void Sprite::UpdateMatrix()
{
// 描画範囲を含めたスケール値
XMFLOAT2 fScale = {
m_Scale.x * m_Rect.z,
m_Scale.y * m_Rect.w
};
float width = static_cast<float>(m_pTexture->GetWidth()) * fScale.x;
float height = static_cast<float>(m_pTexture->GetHeight()) * fScale.y;
// 移動
float x = -1.f + (static_cast<float>(m_Position.x * 2) + width) / static_cast<float>(WINDOW_WIDTH);
float y = 1.f - (static_cast<float>(m_Position.y * 2) + height) / static_cast<float>(WINDOW_HEIGHT);
XMMATRIX posMat = XMMatrixTranslation(x, y, 0.0f);
// 拡縮
XMMATRIX scaleMat = XMMatrixScaling(fScale.x, fScale.y, 1.f);
// ウィンドウ座標にする
XMMATRIX windowScaleMat = XMMatrixScaling(
1.0f / static_cast<float>(WINDOW_WIDTH),
1.0f / static_cast<float>(WINDOW_HEIGHT),
1.f
);
// 回転
XMMATRIX rotMat = XMMatrixRotationZ(m_RotateZ);
// 3つの行列をかけ合わせる
XMMATRIX mat = scaleMat * rotMat * windowScaleMat * posMat;
// XMMATRIX:行優先(row major)
// HLSL:列優先(column major)
// HLSLに合わせるので転置する
*m_pMatrix = XMMatrixTranspose(mat);
}
設定関数を追加
あとは外から設定できる関数を追加するだけです。
// 描画範囲を設定
// 0~テクスチャ幅高で設定できる
void Sprite::SetRect(const RECT& rRect)
{
if (m_pTexture == nullptr) return;
float width = static_cast<float>(m_pTexture->GetWidth());
float height = static_cast<float>(m_pTexture->GetHeight());
XMFLOAT4 rect = {
static_cast<float>(rRect.left) / width,
static_cast<float>(rRect.top) / height,
static_cast<float>(rRect.right) / width,
static_cast<float>(rRect.bottom) / height
};
m_Rect =
*m_pRect = rect;
}
// 0.0f~1.0fで設定できる
void Sprite::SetRect(float x, float y, float w, float h)
{
if (m_pTexture == nullptr) return;
XMFLOAT4 rect = { x, y, w, h };
m_Rect =
*m_pRect = rect;
}
// 色変更
void Sprite::SetColor(UINT8 r, UINT8 g, UINT8 b, UINT8 a)
{
m_Color.x = static_cast<float>(r) / static_cast<float>(UINT8_MAX);
m_Color.y = static_cast<float>(g) / static_cast<float>(UINT8_MAX);
m_Color.z = static_cast<float>(b) / static_cast<float>(UINT8_MAX);
m_Color.w = static_cast<float>(a) / static_cast<float>(UINT8_MAX);
*m_pColor = m_Color;
}
void Sprite::SetRed(UINT8 r)
{
m_Color.x = static_cast<float>(r) / static_cast<float>(UINT8_MAX);
*m_pColor = m_Color;
}
void Sprite::SetGreen(UINT8 g)
{
m_Color.y = static_cast<float>(g) / static_cast<float>(UINT8_MAX);
*m_pColor = m_Color;
}
void Sprite::SetBlue(UINT8 b)
{
m_Color.z = static_cast<float>(b) / static_cast<float>(UINT8_MAX);
*m_pColor = m_Color;
}
void Sprite::SetAlpha(UINT8 a)
{
m_Color.w = static_cast<float>(a) / static_cast<float>(UINT8_MAX);
*m_pColor = m_Color;
}
さいごに
これでUVアニメーションとカラー変更の対応ができました!
そこそこ使えるようになってきているので、簡単な2Dゲームなら作成できるのでは?と思ったり…
ひと通り2D関連でやりたいことは終わったので、次回は3Dモデルを対応していきたい!
コメント