[C++]DirectX 12 3D游戏开发实战—第8章 学习笔记02 2019.5.1

这篇博客主要介绍了在DirectX 12中实现3D游戏开发的材质处理方法,包括将材质数据存储在系统内存和常量缓冲区中,确保GPU能访问到最新的材质数据。同时,讨论了如何在渲染时找到对应的常量数据,并绑定到根签名。此外,还提及了获取法向量对于光照计算的重要性,并预告将探讨不同类型的光源实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

仅作为个人学习使用,请勿转载,谢谢
内容主要为《DirectX 12 3D游戏开发实战》书中记载内容

DirectX 12 3D游戏开发实战—第8章 学习笔记02

词汇

8.9材质的实现

龙书配图8.22 一辆汽车的网格可以分成5中材质属性组
1.目前在绘制调用时允许对材质进行频繁修改

std::unordered_map<std::string, std::unique_ptr<Material>> mMaterials;
void LitWaveApp::BuildMaterials()
{
	auto grass = std::make_unique<Material>();
	grass->Name = "grass";
	grass->MatCBIndex = 0;
	grass->DiffuseAlbedo = XMFLOAT4(0.2f,0.6f,0.2f,1.0f);
	grass->FresnelR0 = XMFLOAT3(0.01f,0.01f,0.01f,0.01f);
	grass->Roughness = 0.125f;
	//当前这种水的材质定义的并不是很好,但是由于我们还未学会所需的全部渲染工具(如透明度、环境反射等),
	//因此暂时先用这些数据解当务之急。
	auto water = std::make_unique<Material>();
	water->Name = "water";
	water->MatCBIndex = 1;
	water->DiffuseAlbedo = XMFLOAT4(0.0f,0.2f,0.6f,1.0f);
	water->FresnelR0 = XMFLOAT3(1.0f,1.0f,1.0f);
	water->Roughness = 0.0f;

	mMaterials["grass"] = std::move(grass);
	mMaterials["water"] = std::move(water);
}

通过上述的表,可以将材质存放于系统内存中。
2.为了令GPU能在着色器中访问到这些材质数据,还需将相关数据复制到常量缓冲区中,就像之前对物体常量缓冲区(per-object constant buffer)一样,将存有每个材质常量的常量缓冲区添加到每个帧资源之中:

struct MaterialConstants
{
	DirectX::XMFLOAT4 DiffuseAlbedo = {1.0f,1.0f,1.0f,1.0f};
	DirectX::XMFLOAT3 FresnelR0 = {0.01f,0.01f,0.01f};
	float Roughness = 0.25f;

	//在纹理贴图章节中会用到
	DirectX::XMFLOAT4X4 MatTransform = MathHelper::Indentity4x4();
}
struct FrameResource
{
public:
...
	std::unique_ptr<UploadBuffer<MaterialConstants>> MaterialCB = nullptr;
...
};

3.在更新函数中,当材质数据有了变化时,便会将其复制到常量缓冲区的对应子区域内,因此GPU材质常量缓冲区中的数据总是与系统内存中的最新材质数据保持一致:

void LitWatesApp::UpdateMaterialCBs(const GameTimer& gt)
{
	auto currMaterialCB = mCurrFrameResource->MaterialCB.get();
	for(auto& e : mMaterials)
	{
		//如果材质常量数据有了变化就更新常量数据缓冲区数据。一旦常量缓冲区数据发生改变,就需对每一个帧资源进行更新
		Material* mat = e.second.get();
		if(mat->NumFramesDirty > 0)
		{
			XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);

			MaterialConstants matConstants;
			matConstants.DiffuseAlbedo = mat->DiffuseAlbedo;
			matConstants.FresnelR0 = mat->FresnelR0;
			matConstants.Roughness = mat->Roughness;

			currMaterialCB->CopyData(mat->MatCBIndex,matConstants);
			//也需对下一个帧资源进行更新
			mat->NumFramesDirty--;
			
		}
	}
}

4.到现在为止每个渲染项都有一个指向Material结构体的指针了,多个渲染项可以引用相同的Material材质,而每个Material对象都存有一个索引,用于在材质常量缓冲区中指定自己的常量数据。至此就能在渲染时找到对应的常量数据的虚拟地址并将它和所需材质常量数据的根描述符进行绑定(也可以通过偏移到堆中的CBV描述符的方式来设置一个描述符表。但是在例程中定义的根签名是采用的材质常量缓冲区的描述符而非描述符表,所以这个例程里这个方法行不通)

void LitWavesApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
{
	UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));//获得物体常量缓冲区大小
	UINT matCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(MaterialConstants));//获得材质常量缓冲区大小
	//创建两种常量缓冲区资源,并指定为当前的帧资源
	auto objectCB = mCurrFrameResource->ObjectCB->Resource();
	auto matCB = mCurrFrameResource->MaterialCB->Resource();

	// For each render item...
	//针对每个渲染项……
	for(size_t i = 0; i < ritems.size(); ++i)//遍历每个渲染项
	{
		auto ri = ritems[i];

		cmdList->IASetVertexBuffers(0, 1, &ri->Geo->VertexBufferView());//设置顶点缓冲区
		cmdList->IASetIndexBuffer(&ri->Geo->IndexBufferView());//设置索引缓冲区
		cmdList->IASetPrimitiveTopology(ri->PrimitiveType);//设置图元拓扑
		
		//获取对应常量数据的虚拟地址
		D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress() + ri->ObjCBIndex*objCBByteSize;
		//找到对应常量数据的虚拟地址
		D3D12_GPU_VIRTUAL_ADDRESS matCBAddress = matCB->GetGPUVirtualAddress() + ri->Mat->MatCBIndex*matCBByteSize;

		cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
		//将材质常量的虚拟地址与材质的常量数据的根描述符相绑定
		cmdList->SetGraphicsRootConstantBufferView(1, matCBAddress);

		cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
	}
}

*需要获取三角形网格表面上每一点处的法向量以此确定光线射向网格表面点处的角度(用于朗伯余弦定律),而为了获取三角形网格表面上每一个点处的近似法向量,则需要在顶点这一层来指定法线,在三角形光栅化的过程中利用这些顶点的法线进行插值计算。

接下来将学习如何实现具体光源类型(平行光源、点光源、聚光灯光源)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值