DX12游戏开发实战习题 第七章
习题的仓库链接如下:oursuccess/D3D12 (github.com)
下面是每道习题的思路解答:
1. 以CreateGeoSphere方法替代例程中的原始CreateSphere方法
思路:
简单按照题目要求进行修改即可
代码:
//在BuildShapesGeometry方法中
#pramga region Quiz0701
//GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 20, 20);
GeometryGenerator::MeshData sphere = geoGen.CreateSphere(0.5f, 1); //第二个参数可以替换为其他值
#pragma endregion
2. 以根常量代替描述符表来设置世界矩阵
思路:
将根描述符表直接替换为根常量即可。 需要注意,由于每个根常量均为1DWORD,因此对于4X4的世界矩阵,我们需要声明一个大小为16的数组。
代码:
//直接移除掉UpdateObjectCBs方法的声明、定义和调用。 不再给出示例(定义中关于world的更新可以保留,我们会将其转移到DrawRenderItems中)
//因为我们不再使用单个物体的常量缓冲区,因此,PassCB现在不再需要偏移。我们可以修改PassCB的Offset,也可以如下简单注释一行:
//在BuildDDescriptorHeaps方法中
#pragma region Quiz0702
//由于现在只有逐帧的常量缓冲区,因此passCB现在不再需要偏移了。mPassCbvOffset默认为0
//mPassCbvOffset = objCount * gNumFrameResources;
#pragma endregion
//因为不再需要单个物体的常量缓冲区,因此我们可以在BuildConstantBufferViews方法中注释掉如下代码:
#pragma region Quiz0702
//由于世界矩阵不再通过描述符表设置,因此单物体的常量缓冲区也不再需要了
/*
//计算该常量缓冲区的大小
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
//计算物体总数
UINT objCount = (UINT)mOpaqueRitems.size();
//每个物体在每个帧资源中都需要一个常量缓冲区
for (int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex)
{
auto objectCB = mFrameResources->ObjectCB->Resource();
for (UINT i = 0; i < objCount; ++i)
{
//获取GPU的虚拟地址
D3D12_GPU_VIRTUAL_ADDRESS cbAddress = objectCB->GetGPUVirtualAddress();
cbAddress += i * objCBByteSize;
//不同的渲染对象理论上来说需要不同的地址偏移
int heapIndex = frameIndex * objCount + i;
auto handle = CD3DX12_CPU_DESCRIPTOR_HANDLE(mCbvHeap->GetCPUDescriptorHandleForHeapStart());
handle.Offset(heapIndex, mCbvSrvUavDescriptorSize);
//创建常量缓冲区描述符,并指定其大小和位置
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
cbvDesc.BufferLocation = cbAddress;
cbvDesc.SizeInBytes = objCBByteSize;
//根据常量缓冲区描述符创建实际的常量缓冲区
md3dDevice->CreateConstantBufferView(&cbvDesc, handle);
}
}
*/
#pragma endregion
//在创建根签名时,我们将第一个描述符表修改为根常量
//BuildRootSignature方法中
//创建根签名
//创建一个有一个元素的根参数
CD3DX12_ROOT_PARAMETER slotRootParameter;
#pragma region Quiz0702
//我们使用根常量代替了描述符表
/*
//创建一个有1个元素的描述符表
CD3DX12_DESCRIPTOR_RANGE cbvTable0;
cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
//使用该描述符表初始化根参数
slotRootParameter.InitAsDescriptorTable(1, &cbvTable0);
*/
//创建一个有16个常量的根常量
slotRootParameter.InitAsConstants(16, 0);
#pragma endregion
//最后,我们修改DrawRenderItems中的方法,现在不再需要设置描述符表的偏移,而是将常量直接绑定到根签名
#pragma region Quiz0702
//因为物体的世界矩阵并不是通过常量缓冲区传入的,因此我们不再需要这里的偏移了
/*
//calc offset
UINT cbvIndex = mCurrFrameResourceIndex * (UINT)mOpaqueRitems.size() + ri->ObjCBIndex;
auto cbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
cbvHandle.Offset(cbvIndex, mCbvSrvUavDescriptorSize);
cmdList->SetGraphicsRootDescriptorTable(0, cbvHandle);
*/
//取而代之的是,我们直接将缓冲区与根常量绑定
XMMATRIX world = XMLoadFloat4x4(&ri->World);
ObjectConstants objConstants;
XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
cmdList->SetGraphicsRoot32BitConstants(0, 16, &objConstants, 0);
#pragma endregion3. 导入骷髅头
思路:
在mAllRitems中添加一个骷髅头的数据即可。我们需要自己读入一下骷髅头的数据。
代码:
//添加一个BuildSkullGeometry方法,并在Initialize时调用
void BuildQuiz03Geometry();
#pragma region Quiz0703
//添加一个添加骷髅头的方法
void BuildSkullGeometry();
#pragma endregion
//BuildSkullGeometry的实现:
#pragma region Quiz0703
//添加一下骷髅头 里面的实现和顶点数据的构建是相同的
void Quiz03::BuildSkullGeometry()
{
std::ifstream fin(&#34;Models/skull.txt&#34;);
if (!fin)
{
MessageBox(0, L&#34;Models/skull.txt not found!&#34;, 0, 0);
return;
}
//读取顶点数量和三角形数量
UINT vcount = 0, tcount = 0;
std::string ignore; //我们将无需的内容读到ignore中
fin >> ignore >> vcount; //fin会逐单词读取。在这里忽略了VertexCount:,将31076读入了vcount
fin >> ignore >> tcount;
fin >> ignore >> ignore >> ignore >> ignore; //将VetexList (pos, normal)与下一行的{ 忽略
//这里的Vertex添加了Normal数据
std::vector<Vertex> vertices(vcount);
for (UINT i = 0; i < vcount; ++i)
{
fin >> vertices.Pos.x >> vertices.Pos.y >> vertices.Pos.z;
fin >> vertices.Normal.x >> vertices.Normal.y >> vertices.Normal.z;
}
//准备读取indices 同样要跳过无用的数据
fin >> ignore >> ignore >> ignore;
std::vector<std::int32_t> indices(3 * tcount);
for (UINT i = 0; i < tcount; ++i)
{
//一个三角形有三个顶点
fin >> indices >> indices >> indices;
}
//读取完毕 关闭文件
fin.close();
//将indices和vertices打包到常量缓冲区
const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);
const UINT ibByteSize = (UINT)indices.size() * sizeof(std::int32_t);
auto geo = std::make_unique<MeshGeometry>();
geo->Name = &#34;skullGeo&#34;;
ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);
ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);
geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);
geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);
geo->VertexByteStride = sizeof(Vertex);
geo->VertexBufferByteSize = vbByteSize;
geo->IndexFormat = DXGI_FORMAT_R32_UINT;
geo->IndexBufferByteSize = ibByteSize;
SubmeshGeometry skullMesh;
skullMesh.IndexCount = (UINT)indices.size();
skullMesh.StartIndexLocation = 0;
skullMesh.BaseVertexLocation = 0;
geo->DrawArgs[&#34;skull&#34;] = skullMesh;
mGeometries = std::move(geo);
}
#pragma endregion
//在BuildRenderItems方法中,将该骷髅头加入到mAllRitems中(这里附带了一些上下文,方便定位)
gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs[&#34;grid&#34;].StartIndexLocation;
gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs[&#34;grid&#34;].BaseVertexLocation;
mAllRitems.push_back(std::move(gridRitem));
#pragma region Quiz0703
//将骷髅头填充到这里
auto skullRitem = std::make_unique<RenderItem>();
skullRitem->World = MathHelper::Identity4x4();
skullRitem->ObjCBIndex = 2;
const auto skullGeo = mGeometries[&#34;skullGeo&#34;].get();
const auto skull = skullGeo->DrawArgs[&#34;skull&#34;];
skullRitem->Geo = skullGeo;
skullRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
skullRitem->IndexCount = skull.IndexCount;
skullRitem->StartIndexLocation = skull.StartIndexLocation;
skullRitem->BaseVertexLocation = skull.BaseVertexLocation;
mAllRitems.push_back(std::move(skullRitem));
//这里的2被改为3 因为我们在上面添加了一个骷髅头
UINT objCBIndex = 3;
#pragma endregion如果往后看几章,我们可以发现,书中有骷髅头读取的实现...
页:
[1]