【第1部】シンプルバージョン編
1203.複数のオブジェクトの描画(3)
前項に引き続き
Simplesample102です。
Scene::OnInitAssets()関数
Scene::OnInitAssets()関数は、このサンプルで配置されるオブジェクトを初期化します。
このサンプルでは、
静止している三角形と
移動する三角形と
移動する四角形の3つのオブジェクトを描画するわけですが、それぞれ
クラスとして定義されています。
それらは
Scene.h/cppに宣言、定義されています。
まず
Sceneクラスですが、以下のように宣言されています。
class Scene :public SceneBase {
FixedTriangle m_FixedTriangle;
MoveTriangle m_MoveTriangle;
MoveSquare m_MoveSquare;
public:
Scene() :SceneBase() {}
virtual ~Scene() {}
virtual void OnInit()override;
virtual void OnInitAssets()override;
virtual void OnUpdate()override;
virtual void OnDraw()override;
virtual void OnDestroy()override;
};
ここで宣言されている
FixedTriangle m_FixedTriangle;
MoveTriangle m_MoveTriangle;
MoveSquare m_MoveSquare;
がそれぞれのオブジェクトです。
それらを初期化するのが
Scene::OnInitAssets()関数です。
void Scene::OnInitAssets() {
// それぞれのオブジェクトの初期化
{
m_FixedTriangle.OnInit();
m_MoveTriangle.OnInit();
m_MoveSquare.OnInit();
}
}
FixedTriangleクラス
まず、
静止している三角形から説明します。
Scene.hにあります
FixedTriangleが
静止している三角形です。以下に宣言部を紹介します。
class FixedTriangle {
//PositionColorの三角形メッシュ
shared_ptr<BaseMesh> m_pcTriangleMesh;
//PositionColor用パイプラインステート(コンスタントバッファなし)
ComPtr<ID3D12PipelineState> m_pcPipelineState;
public:
FixedTriangle() {}
~FixedTriangle() {}
void OnInit();
void OnUpdate() {}
void OnDraw();
};
このサンプルでは、オブジェクトごとにクラスを作成しますので、メッシュは
shared_ptr<BaseMesh> m_pcTriangleMesh;
という形で準備します。また、パイプラインステートも準備します。
初期化そのものは
FixedTriangle::OnInit()で行います。
void FixedTriangle::OnInit() {
auto baseDevice = App::GetBaseDevice();
auto commandList = baseDevice->GetCommandList();
auto aspectRatio = baseDevice->GetAspectRatio();
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeLineDesc;
//コンスタントバッファ無し
m_pcPipelineState
= PipelineState::CreateDefault2D<VertexPositionColor, VSPCSprite, PSPCSprite>(
baseDevice->GetRootSignature(), pipeLineDesc
);
vector<VertexPositionColor> vertex =
{
{ Float3(0.0f, 0.25f * aspectRatio, 0.0f), Float4(1.0f, 0.0f, 0.0f, 1.0f) },
{ Float3(0.25f, -0.25f * aspectRatio, 0.0f), Float4(0.0f, 1.0f, 0.0f, 1.0f) },
{ Float3(-0.25f, -0.25f * aspectRatio, 0.0f), Float4(0.0f, 0.0f, 1.0f, 1.0f) }
};
//三角形メッシュ作成
m_pcTriangleMesh = BaseMesh::CreateBaseMesh<VertexPositionColor>(vertex);
}
このオブジェクトは
SimpleSample101で紹介したものとほぼおなじです。
シェーダも同じ内容のシェーダを使っています。
MoveTriangleクラス
MoveTriangleクラスは左から右に流れるように動く三角形です。
以下が宣言部です。
class MoveTriangle {
//PositionColorの三角形メッシュ
shared_ptr<BaseMesh> m_pcTriangleMesh;
//PositionColor用パイプラインステート(コンスタントバッファあり)
ComPtr<ID3D12PipelineState> m_pcConstPipelineState;
//コンスタントバッファ構造体の実体
SceneConstantBuffer m_constantBufferData;
//コンスタントバッファ
shared_ptr<ConstantBuffer> m_ConstantBuffer;
//コンスタントバッファのインデックス
UINT m_constBuffIndex;
public:
MoveTriangle(){}
~MoveTriangle() {}
void OnInit();
void OnUpdate();
void OnDraw();
};
赤くなっている部分が、今回注目してほしい部分です。
SimpleSample101では、オブジェクトは変化しませんでしたが、
MoveTriangleは移動します。
移動を表現するためには、シェーダーに対して
描画する場所を変化させなければなりません。
シェーダに対して、そういうパラメータを渡す領域が
コンスタントバッファです。
コンスタントバッファについては
1201.複数のオブジェクトの描画(1)で説明した
CbvSrvUavデスクプリタヒープを思い出してください。
BaeDx12では
CbvSrvUavデスクプリタヒープはデフォルトで
1024個作成する、と説明しました。
つまり、
GameDevice::LoadPipeline()ですでに、この数のデスクプリタヒープは作成済みです。
ですので、
MoveTriangleの初期化のときは、ここから、このオブジェクトで使用する
CbvSrvUavデスクプリタヒープを確定させます。
それは
MoveTriangle::OnInit()関数で行います。
void MoveTriangle::OnInit() {
auto baseDevice = App::GetBaseDevice();
auto commandList = baseDevice->GetCommandList();
auto aspectRatio = baseDevice->GetAspectRatio();
D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeLineDesc;
m_pcConstPipelineState
= PipelineState::CreateDefault2D<VertexPositionColor, VSPCConstSprite, PSPCConstSprite>(
baseDevice->GetRootSignature(),
pipeLineDesc
);
vector<VertexPositionColor> vertex =
{
{ Float3(0.0f, 0.25f * aspectRatio, 0.0f), Float4(0.0f, 1.0f, 0.0f, 1.0f) },
{ Float3(0.25f, -0.25f * aspectRatio, 0.0f), Float4(0.0f, 0.0f, 1.0f, 1.0f) },
{ Float3(-0.25f, -0.25f * aspectRatio, 0.0f), Float4(1.0f, 0.0f, 0.0f, 1.0f) }
};
//三角形メッシュ作成
m_pcTriangleMesh = BaseMesh::CreateBaseMesh<VertexPositionColor>(vertex);
//コンスタントバッファハンドルを作成
m_constBuffIndex = baseDevice->GetCbvSrvUavNextIndex();
CD3DX12_CPU_DESCRIPTOR_HANDLE Handle(
baseDevice->GetCbvSrvUavDescriptorHeap()->GetCPUDescriptorHandleForHeapStart(),
m_constBuffIndex,
baseDevice->GetCbvSrvUavDescriptorHandleIncrementSize()
);
m_ConstantBuffer = ConstantBuffer::CreateDirect(Handle, m_constantBufferData);
}
三角形メッシュの作成までは
FixedTriangleクラスと同じです。赤くなっている部分が
コンスタントバッファい関連する部分です。
まず
m_constBuffIndex = baseDevice->GetCbvSrvUavNextIndex();
で
CbvSrvUavデスクプリタヒープの
次のインデックスを取得します。この関数は
CbvSrvUavデスクプリタヒープの中から、
使ってない場所のインデックスを返します。最初は
0です。2個目は
13個目は
2が帰ってきます。
次のインデックスを取得したら
CD3DX12_CPU_DESCRIPTOR_HANDLE Handle(
baseDevice->GetCbvSrvUavDescriptorHeap()->GetCPUDescriptorHandleForHeapStart(),
m_constBuffIndex,
baseDevice->GetCbvSrvUavDescriptorHandleIncrementSize()
);
で、そのハンドル(ディスクプリタハンドル)を作成します。上記の設定で、このオブジェクトが使用するハンドルを作れます。
そのご、そのハンドルをもとに
m_ConstantBuffer = ConstantBuffer::CreateDirect(Handle, m_constantBufferData);
と
コンスタントバッファを作成します。
ConstantBuffer::CreateDirect()関数はテンプレート関数になっていて、以下のような形です。
template<typename T>
static inline shared_ptr<ConstantBuffer> CreateDirect(
const CD3DX12_CPU_DESCRIPTOR_HANDLE& descHandle,
const T& src
) {
//デバイスの取得
auto device = App::GetID3D12Device();
shared_ptr<ConstantBuffer> ptrConst = shared_ptr<ConstantBuffer>(new ConstantBuffer());
// コンスタントバッファのサイズは256バイト境界ごとに作成する
UINT constsize = (sizeof(T) + 255) & ~255;
ThrowIfFailed(device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(constsize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&ptrConst->m_constantBuffer)));
// コンスタントバッファビューの作成
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = ptrConst->m_constantBuffer->GetGPUVirtualAddress();
cbvDesc.SizeInBytes = constsize;
device->CreateConstantBufferView(&cbvDesc, descHandle);
// アプリケーションが動作しているあいだ中、Map状態でも問題ない
CD3DX12_RANGE readRange(0, 0);
ThrowIfFailed(ptrConst->m_constantBuffer->Map(
0,
&readRange,
reinterpret_cast<void**>(&ptrConst->m_pCbvDataBegin))
);
memcpy(ptrConst->m_pCbvDataBegin, &src, sizeof(src));
return ptrConst;
}
テンプレートである
typename Tは、実引数では
const T& srcになります。
今回は
m_ConstantBuffer = ConstantBuffer::CreateDirect(Handle, m_constantBufferData);
と呼びだしているので、
m_constantBufferDataの型、ということになります。
この型は
Scene.hに宣言されている構造体で、
struct SceneConstantBuffer
{
XMFLOAT4 offset;
SceneConstantBuffer() :
offset(0.0f, 0, 0, 0)
{}
};
となっています。
XMFLOAT4は
DirectXMathで宣言されている型です。
struct XMFLOAT4
{
float x;
float y;
float z;
float w;
XMFLOAT4() = default;
XMFLOAT4(const XMFLOAT4&) = default;
XMFLOAT4& operator=(const XMFLOAT4&) = default;
XMFLOAT4(XMFLOAT4&&) = default;
XMFLOAT4& operator=(XMFLOAT4&&) = default;
XM_CONSTEXPR XMFLOAT4(float _x, float _y, float _z, float _w) :
x(_x), y(_y), z(_z), w(_w) {}
explicit XMFLOAT4(_In_reads_(4) const float *pArray) :
x(pArray[0]), y(pArray[1]), z(pArray[2]), w(pArray[3]) {}
};
こんな感じになっています。シェーダーの入力と互換が取れているので、この型になっています。
ConstantBufferは
クラスです。
CreateDirect()static関数によってインスタンスを作成し、必要なリソースを作成します。
UINT constsize = (sizeof(T) + 255) & ~255;
ThrowIfFailed(device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(constsize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&ptrConst->m_constantBuffer)));
と作成するわけですが、作成するリソースのサイズを
constsizeに設定しますが、その際
UINT constsize = (sizeof(T) + 255) & ~255;
とします。これは
コンスタントバッファは256バイト単位で作成しなければならないからです。
その後
CreateCommittedResource()関数でリソースを作成します。それは
ConstantBufferクラスの
m_constantBufferに設定されます。
続いて
コンスタントバッファビューを作成します。
// コンスタントバッファビューの作成
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = ptrConst->m_constantBuffer->GetGPUVirtualAddress();
cbvDesc.SizeInBytes = constsize;
device->CreateConstantBufferView(&cbvDesc, descHandle);
ここで、先ほど作成した
リソースの
GPU側仮想アドレスと、
descHandleを結びつけます。
これで
ディスクリプタヒープ(ハンドル経由)、リソースが関連づけられ
コンスタントバッファビューが作成されます。
最後に、コンスタントバッファと、CPU側のポインタ
ptrConst->m_pCbvDataBeginをマップし、データの内容をコピーします。
// アプリケーションが動作しているあいだ中、Map状態でも問題ない
CD3DX12_RANGE readRange(0, 0);
ThrowIfFailed(ptrConst->m_constantBuffer->Map(
0,
&readRange,
reinterpret_cast<void**>(&ptrConst->m_pCbvDataBegin))
);
memcpy(ptrConst->m_pCbvDataBegin, &src, sizeof(src));
ここで、コメントにあるように、マップした領域は、アプリケーションが終了するまでマップしたままで問題ありません。
これで
MoveTriangle::OnInit()は、終わりです。