您的位置:首页 > 编程语言 > C语言/C++

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

2019-05-01 02:27 489 查看
版权声明:个人学习使用,请勿转载 https://blog.csdn.net/qq_36028073/article/details/89735220

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

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

词汇

8.9材质的实现


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);
}
}

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

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐