DirectX12の定数バッファについて質問です

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ガマ太郎
記事: 3
登録日時: 2年前

DirectX12の定数バッファについて質問です

#1

投稿記事 by ガマ太郎 » 2年前

初めまして。初めて質問させていただきます。至らない点があるかと思いますがご容赦ください。

現在「DirectX12の魔導書 3Dレンダリングの基礎からMMDモデルを踊らせるまで」という本を読みながらDirectX12を勉強中です。
MMDモデルを表示し、マテリアルやライティングまでは実装できましたが、リファクタリングをしている最中で定数バッファに問題が置きました。

【やりたかったこと】
定数バッファを、「視点用(ビュー・プロジェクション・視点)」「オブジェクト座標用(ワールドマトリクス)」「マテリアル用(マテリアル)」の3つに分けて、処理を分割しようとしました。
()内はGPUにマップしているものです。

視点用定数バッファで使用している構造体

コード:

typedef struct
{
    XMFLOAT4X4 view;
    XMFLOAT4X4 proj;
    XMFLOAT3 eye;   // 視線座標
}SCENEMATRIX;
視点用定数バッファ生成

コード:

HRESULT Object3D::CreateSceneCBuffer(MODEL_DX12* Model){
	auto buffersize = sizeof(SCENEMATRIX);
	buffersize = (buffersize + 0xff) & ~0xff;
	CD3DX12_HEAP_PROPERTIES cd_hp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);	
	CD3DX12_RESOURCE_DESC cd_buffer = CD3DX12_RESOURCE_DESC::Buffer(buffersize);

	HRESULT hr = DX12Renderer::GetDevice()->CreateCommittedResource(
		&cd_hp,
		D3D12_HEAP_FLAG_NONE,
		&cd_buffer,
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
	I	ID_PPV_ARGS(Model->SceneConstBuffer.ReleaseAndGetAddressOf()));

	// 視線
	XMFLOAT3 eye(0, 15, -15);
	// 注視点
	XMFLOAT3 target(0, 15, 0);
	// 上ベクトル
	XMFLOAT3 v_up(0, 1, 0);

	XMMATRIX viewMat  = XMMatrixLookAtLH(XMLoadFloat3(&eye), XMLoadFloat3(&target), 							 XMLoadFloat3(&v_up));
	XMMATRIX projMat = XMMatrixPerspectiveFovLH(
		XM_PIDIV4,//画角は90°
		static_cast<float>(SCREEN_WIDTH) / static_cast<float>(SCREEN_HEIGHT),//アス比
		0.1f,//近い方
		1000.0f//遠い方);

	hr = Model->SceneConstBuffer->Map(0, nullptr, (void**)&Model->SceneMatrix);

	XMStoreFloat4x4(&Model->SceneMatrix->view, viewMat);
	XMStoreFloat4x4(&Model->SceneMatrix->proj, projMat);
	Model->SceneMatrix->eye = eye;

	// ディスクリプタヒープ作成
	D3D12_DESCRIPTOR_HEAP_DESC dhd = {};
	dhd.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	dhd.NodeMask = 0;
	dhd.NumDescriptors = 1;
	dhd.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	hr = DX12Renderer::GetDevice()->CreateDescriptorHeap(&dhd, IID_PPV_ARGS(Model->sceneDescHeap.ReleaseAndGetAddressOf()));

	D3D12_CONSTANT_BUFFER_VIEW_DESC cbvd = {};
	cbvd.BufferLocation = Model->SceneConstBuffer.Get()->GetGPUVirtualAddress();
	cbvd.SizeInBytes = Model->SceneConstBuffer.Get()->GetDesc().Width;

	auto heapHandle = Model->sceneDescHeap.Get()->GetCPUDescriptorHandleForHeapStart();
	DX12Renderer::GetDevice()->CreateConstantBufferView(&cbvd, heapHandle);
	return hr;
}
オブジェクト用定数バッファで使用している構造体

コード:

typedef struct
{
    XMFLOAT4X4 world;
}TRANSFORM;
オブジェクト用定数バッファ生成

コード:

HRESULT Object3D::CreateTransformCBuffer(MODEL_DX12* Model){
	auto bufferSize = sizeof(TRANSFORM);
	bufferSize = (bufferSize + 0xff) & ~0xff;
	auto cd_hp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
	auto cd_rd = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);

	HRESULT hr = DX12Renderer::GetDevice()->CreateCommittedResource(
		&cd_hp,
		D3D12_HEAP_FLAG_NONE,
		&cd_rd,
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
		IID_PPV_ARGS(Model->TransfromConstBuffer.ReleaseAndGetAddressOf()));

	XMMATRIX WorldMatrix = XMMatrixIdentity();

	// マップ
	hr = Model->TransfromConstBuffer.Get()->Map(0, nullptr, (void**)&Model->TransformMatrix);
	XMStoreFloat4x4(&Model->TransformMatrix->world, WorldMatrix);

	D3D12_DESCRIPTOR_HEAP_DESC transform_dhd = {};
	transform_dhd.NumDescriptors = 1;
	transform_dhd.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	transform_dhd.NodeMask = 0;
	transform_dhd.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	hr = DX12Renderer::GetDevice()->CreateDescriptorHeap(&transform_dhd, IID_PPV_ARGS(Model->transformDescHeap.ReleaseAndGetAddressOf()));

	D3D12_CONSTANT_BUFFER_VIEW_DESC cdvDesc = {};
	cdvDesc.BufferLocation = Model->TransfromConstBuffer.Get()->GetGPUVirtualAddress();
	cdvDesc.SizeInBytes = bufferSize;

	auto heapHandle = Model->transformDescHeap.Get()->GetCPUDescriptorHandleForHeapStart();
	DX12Renderer::GetDevice()->CreateConstantBufferView(&cdvDesc, heapHandle);
	return hr;
}
ルートシグネチャ作成

コード:

HRESULT DX12Renderer::CreateRootSignature(){
	// ディスクリプタレンジテーブル作成
	// 定数(b0)(ビュープロジェクション用)
	CD3DX12_DESCRIPTOR_RANGE dr[4] = {};
	dr[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
	
	// 定数(b1)(ワールド・ボーン用)
	dr[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);

	// マテリアル(b2)
	dr[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 2);

	// テクスチャ用(マテリアルとペア)
	dr[3].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 4, 0);

	// ルートパラメータ作成
	D3D12_ROOT_PARAMETER rootparam[3] = {};
	rootparam[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
	rootparam[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;	// 全シェーダーから見える
	rootparam[0].DescriptorTable.pDescriptorRanges = &dr[0];	// ディスクリプタレンジのアドレス
	rootparam[0].DescriptorTable.NumDescriptorRanges = 1;	// ディスクリプタレンジ数

	// ワールド・ボーン
	rootparam[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
	rootparam[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;	// 頂点シェーダーから見える
	rootparam[1].DescriptorTable.pDescriptorRanges = &dr[1];	// ディスクリプタレンジのアドレス
	rootparam[1].DescriptorTable.NumDescriptorRanges = 1;	// ディスクリプタレンジ数
	
	// マテリアル
	rootparam[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
	rootparam[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
	rootparam[2].DescriptorTable.pDescriptorRanges = &dr[2];
	rootparam[2].DescriptorTable.NumDescriptorRanges = 2;

	CD3DX12_STATIC_SAMPLER_DESC sd[2] = {};
	sd[0].Init(0);	// シェーダーレジスターは0番
	sd[1].Init(1, D3D12_FILTER_ANISOTROPIC, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, 		D3D12_TEXTURE_ADDRESS_MODE_CLAMP);	// シェーダーレジスターは1番、UVはクランプ
	
	// ルートシグネチャー作成
	D3D12_ROOT_SIGNATURE_DESC rsd = {};
	rsd.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
	rsd.pParameters = rootparam;	// ルートパラメータの先頭アドレス
	rsd.NumParameters = 3;			// ルートパラメータ数
	rsd.pStaticSamplers = sd;		// サンプラーステートの先頭アドレス
	rsd.NumStaticSamplers = 2;		// サンプラーステート数

	ID3DBlob* RootSigBlob = nullptr;
	hr = D3D12SerializeRootSignature(&rsd, D3D_ROOT_SIGNATURE_VERSION_1, &RootSigBlob, &m_ErrorBlob);
	hr = m_Device->CreateRootSignature(0, RootSigBlob->GetBufferPointer(), RootSigBlob->GetBufferSize(), 	IID_PPV_ARGS(m_RootSignature.ReleaseAndGetAddressOf()));
	RootSigBlob->Release();
	reutnr hr;
}
描画命令をコマンドリストに登録

コード:

void Object::Draw(){
	// プリミティブトポロジ設定
	DX12Renderer::GetGraphicsCommandList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// 頂点バッファービューセット
	DX12Renderer::GetGraphicsCommandList()->IASetVertexBuffers(0, 1, &m_Model.vbView);

	// インデックスバッファービューセット
	DX12Renderer::GetGraphicsCommandList()->IASetIndexBuffer(&m_Model.ibView);

	// 視点のデスクリプターヒープセット
	ID3D12DescriptorHeap* scene_dh[] = { m_Model.sceneDescHeap.Get() };
	DX12Renderer::GetGraphicsCommandList()->SetDescriptorHeaps(1, scene_dh);
	// ルートパラメータとディスクリプターヒープの関連付け
	auto scene_handle = m_Model.sceneDescHeap.Get()->GetGPUDescriptorHandleForHeapStart();
	DX12Renderer::GetGraphicsCommandList()->SetGraphicsRootDescriptorTable(0, scene_handle);

	ID3D12DescriptorHeap* trans_dh[] = { m_Model.transformDescHeap.Get() };
	DX12Renderer::GetGraphicsCommandList()->SetDescriptorHeaps(1, trans_dh);
	auto transform_handle = m_Model.transformDescHeap.Get()->GetGPUDescriptorHandleForHeapStart();
	DX12Renderer::GetGraphicsCommandList()->SetGraphicsRootDescriptorTable(1, transform_handle);

	// マテリアルのディスクリプタヒープセット
	ID3D12DescriptorHeap* mdh[] = { m_Model.materialDescHeap.Get() };
	DX12Renderer::GetGraphicsCommandList()->SetDescriptorHeaps(1, mdh);

	// マテリアル用ディスクリプタヒープの先頭アドレスを取得
	auto material_handle = m_Model.materialDescHeap.Get()->GetGPUDescriptorHandleForHeapStart();
	unsigned int idxOffset = 0;

	// CBVとSRVとSRVとSRVとSRVで1マテリアルを描画するのでインクリメントサイズを5倍にする
	auto cbvsize = DX12Renderer::GetDevice()->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) * 5;

	for (auto& m : m_Model.material)
	{
		DX12Renderer::GetGraphicsCommandList()->SetGraphicsRootDescriptorTable(2, material_handle);
		DX12Renderer::GetGraphicsCommandList()->DrawIndexedInstanced(m.indicesNum, 1, idxOffset, 0, 0);

		// ヒープポインタとインデックスを次に進める
		material_handle.ptr += cbvsize;
		idxOffset += m.indicesNum;
	}
}
cbufferの内容

コード:

cbuffer SceneView : register(b0)   // 定数バッファー
{
    matrix view;    // ビュー
    matrix proj;    // プロジェクション
    float3 eye; // 視線
};

cbuffer Transform : register(b1)
{
    matrix world;   // ワールド変換行列
};

cbuffer Material : register(b2)
{
    float4 diffuse;
    float4 specular;
    float3 ambient;
};
上記の内容で実行し、Pix on Windowsでシェーダーへ転送内容を見ると、レジスタ番号1のみデータが入りレジスタ番号0にはデータが転送できていないという状況になっていしまいました。
エラーは出ずにexeは起動できますがモデルが全く表示されません。
ビュープロジェクションの内容が転送できてないのかと思いレジスタ番号1に全て転送するように変えてみると、正しく動作するのでレジスタ番号0への転送がうまく行っていないようでした。

上記の方法で定数バッファの設定は正しいのでしょうか。
長文になり申し訳ございません。ご教授いただけると幸いです。

ビルド環境
OS:Windows
コンパイラ:Microsoft VisualStudio2019 ver.16.11.19

アバター
あたっしゅ
記事: 667
登録日時: 14年前
住所: 東京23区
連絡を取る:

Re: DirectX12の定数バッファについて質問です

#2

投稿記事 by あたっしゅ » 2年前

東上☆海美☆「
『DirectX12の魔導書』、持っているが、生の DirectX 、よくわからんみみ。
VTuber:
[香車]東上☆Aho(暎帆)☆海美
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

ガマ太郎
記事: 3
登録日時: 2年前

Re: DirectX12の定数バッファについて質問です

#3

投稿記事 by ガマ太郎 » 2年前

追記
リファクタリング内容は本の内容をコピーしているわけではなく、独自にクラスを作っています。
質問にあるコードを入れたプロジェクトのgitを起きますので、お手数ですが中身を見ていただけると助かります。
投げっぱなしになってしまいますが、それほどなぜこうなっているのかがわからず手をこまねいています・・・

↓プロジェクト↓
https://github.com/souha1008/DirectX12_MMD.git

ガマ太郎
記事: 3
登録日時: 2年前

Re: DirectX12の定数バッファについて質問です

#4

投稿記事 by ガマ太郎 » 2年前

お久しぶりです。
解決しましたので、解決方法を置いておきます。

【原因】
コンスタントバッファーを生成後、コンスタントバッファーと同じディスクリプタヒープを使ってシェーダーリソースビューを作っていました。
結果上書きされてしまい表示できなくなった。

もし同じことで悩んだ方がいたら、この原因を突き詰めて直してみると良いかもしれません。

アバター
あたっしゅ
記事: 667
登録日時: 14年前
住所: 東京23区
連絡を取る:

Re: DirectX12の定数バッファについて質問です

#5

投稿記事 by あたっしゅ » 2年前

東上☆海美☆「
解決したんですね。よかったみみ。

こっちは、いまだに、DirectXTex-master をインクルード/ライブラリ・リンクできてません。
VTuber:
[香車]東上☆Aho(暎帆)☆海美
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

返信

“C言語何でも質問掲示板” へ戻る