找回密码
 立即注册
查看: 181|回复: 0

DX12游戏开发实战习题 第七章

[复制链接]
发表于 2022-12-5 08:20 | 显示全部楼层 |阅读模式
习题的仓库链接如下:
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[frameIndex]->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[2];

#pragma region Quiz0702
        //我们使用根常量代替了描述符表
        /*
        //创建一个有1个元素的描述符表
        CD3DX12_DESCRIPTOR_RANGE cbvTable0;
        cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
        //使用该描述符表初始化根参数
        slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);
        */
        //创建一个有16个常量的根常量
        slotRootParameter[0].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("Models/skull.txt");
        if (!fin)
        {
                MessageBox(0, L"Models/skull.txt not found!", 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[i * 3 + 0] >> indices[i * 3 + 1] >> indices[i * 3 + 2];
        }
        //读取完毕 关闭文件
        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 = "skullGeo";

        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["skull"] = skullMesh;
        mGeometries[geo->Name] = std::move(geo);
}
#pragma endregion

//在BuildRenderItems方法中,将该骷髅头加入到mAllRitems中(这里附带了一些上下文,方便定位)
    gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
    gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].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["skullGeo"].get();
        const auto skull = skullGeo->DrawArgs["skull"];
        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如果往后看几章,我们可以发现,书中有骷髅头读取的实现...
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 17:58 , Processed in 0.064328 second(s), 22 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表