【DX12を学びたい】2DテクスチャのUVアニメーションに対応する

uvアニメーションの結果 DirextX

引き続き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)の場合

範囲設定NG
このような感じではなく
範囲設定OK
こういう風にしたい
// 行列の更新
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モデルを対応していきたい!

かれいど

ゲームをしたり、作ったり
色々な事に挑戦していきたい!

サッカー観戦も趣味で
主にJ1リーグを観ています。

かれいどをフォローする
DirextX
スポンサーリンク
シェアする
かれいどブログ

コメント