Directx12红龙书练习解答-第六章 利用Direct3D绘制几何体
1.写出与下列顶点结构体所对应的D3D12_INPUT_ELEMENT_DESC数组:

2.改写彩色立方体演示程序,这次使用两个顶点缓冲区(以及两个输入槽)来向渲染流水线传送顶点数据。这两个顶点缓冲区,一个用来存储位置元素,另一个用来储存颜色元素。此时,我们应当利用两个顶点结构体以下列方式分别存放这两种不同的数据:
需要修改以下源码:
2.1.新建两个结构体
struct VPosData { XMFLOAT3 Pos; }; struct VColorData { XMFLOAT4 Color; };
2.2.修改D3D12_INPUT_ELEMENT_DESC
mInputLayout = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } };
2.3.修改Draw函数,输入两次顶点,并绑定不同的插槽
mCommandList->IASetVertexBuffers(0, 1, &mBoxGeo->VertexBufferView()); mCommandList->IASetVertexBuffers(1, 1, &mBoxGeo->VertexBufferView());
2.4.修改Shader
struct VPosData { float3 PosL : POSITION; }; struct VColorData { float4 Color : COLOR; }; cbuffer cbPerObject : register(b0) { float4x4 gWorldViewProj; float time; }; //struct VertexIn //{ // float3 PosL : POSITION; // float4 Color : COLOR; //}; struct VertexOut { float4 PosH : SV_POSITION; float4 Color : COLOR; }; VertexOut VS(VPosData vp, VColorData vc) { VertexOut vout; // Transform to homogeneous clip space. vout.PosH = mul(float4(vp.PosL, 1.0f), gWorldViewProj); // Just pass vertex color into the pixel shader. vout.Color = vc.Color ; return vout; } //VertexOut VS(VertexIn vin) //{ // VertexOut vout; // // Transform to homogeneous clip space. // vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj); // // Just pass vertex color into the pixel shader. // vout.Color = vin.Color; // return vout; //} float4 PS(VertexOut pin) : SV_Target { return pin.Color; }
4.构造图6.8所示的金字塔的顶点列表和索引列表,并将其绘制出来:令塔顶为红色,其他底座顶点为绿色。
std::array<Vertex, 5> pyramidVertices = { Vertex({ XMFLOAT3(-1.0f, 0.0f, 1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(-1.0f,0.0f, -1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(+1.0f, 0.0f, -1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(+1.0f, 0.0f, +1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(0.0f, 2.0f, 0.0f), XMFLOAT4(Colors::Red) }), }; std::array<std::uint16_t, 18> pyramidIndices = { // front face 0, 1, 2, 0, 2, 3, 0,4,1, 1,4,2, 2,4,3, 3,4,0 };
//修改上传的顶点和索引
mBoxGeo = std::make_unique<MeshGeometry>(); mBoxGeo->Name = "boxGeo"; ThrowIfFailed(D3DCreateBlob(vbByteSize, &mBoxGeo->VertexBufferCPU)); CopyMemory(mBoxGeo->VertexBufferCPU->GetBufferPointer(), pyramidVertices.data(), vbByteSize); ThrowIfFailed(D3DCreateBlob(ibByteSize, &mBoxGeo->IndexBufferCPU)); CopyMemory(mBoxGeo->IndexBufferCPU->GetBufferPointer(), pyramidIndices.data(), ibByteSize); mBoxGeo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), pyramidVertices.data(), vbByteSize, mBoxGeo->VertexBufferUploader); mBoxGeo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), pyramidIndices.data(), ibByteSize, mBoxGeo->IndexBufferUploader); mBoxGeo->VertexByteStride = sizeof(Vertex); mBoxGeo->VertexBufferByteSize = vbByteSize; mBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT; mBoxGeo->IndexBufferByteSize = ibByteSize; SubmeshGeometry submesh; submesh.IndexCount = (UINT)pyramidIndices.size(); submesh.StartIndexLocation = 0; submesh.BaseVertexLocation = 0; mBoxGeo->DrawArgs["box"] = submesh;
7.将立方体和练习4中金字塔的顶点合并到一个大的顶点缓冲区内,再将两者的索引合并至一个大的索引缓冲区中(但是不要更新索引值)。接着,再根据上述改动来依次调整调用ID3D12GraphicsCommandList::DrawIndexedInstanced方法的参数,以正确地绘制立方体和金字塔。并通过设置世界变换矩阵,使两者在世界空间中互不相交。
7.1.BuildBoxGeometry方法修改
void BoxApp::BuildBoxGeometry() { std::array<Vertex, 8> vertices = { Vertex({ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::White) }), Vertex({ XMFLOAT3(-1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Black) }), Vertex({ XMFLOAT3(+1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Red) }), Vertex({ XMFLOAT3(+1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(-1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Blue) }), Vertex({ XMFLOAT3(-1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Yellow) }), Vertex({ XMFLOAT3(+1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Cyan) }), Vertex({ XMFLOAT3(+1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Magenta) }) }; std::array<std::uint16_t, 36> indices = { // front face 0, 1, 2, 0, 2, 3, // back face 4, 6, 5, 4, 7, 6, // left face 4, 5, 1, 4, 1, 0, // right face 3, 2, 6, 3, 6, 7, // top face 1, 5, 6, 1, 6, 2, // bottom face 4, 0, 3, 4, 3, 7 }; std::array<Vertex, 5> pyramidVertices = { Vertex({ XMFLOAT3(-1.0f, 0.0f, 1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(-1.0f,0.0f, -1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(+1.0f, 0.0f, -1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(+1.0f, 0.0f, +1.0f), XMFLOAT4(Colors::Green) }), Vertex({ XMFLOAT3(0.0f, 2.0f, 0.0f), XMFLOAT4(Colors::Red) }), }; std::array<std::uint16_t, 18> pyramidIndices = { // front face 0, 1, 2, 0, 2, 3, 0,4,1, 1,4,2, 2,4,3, 3,4,0 }; UINT boxVertexOffset = 0; UINT pyramidVertexOffset = (UINT)vertices.size(); UINT boxIndexOffset = 0; UINT pyramidIndexOffset = (UINT)indices.size(); SubmeshGeometry boxSubmesh; boxSubmesh.IndexCount = indices.size(); boxSubmesh.StartIndexLocation = boxIndexOffset; boxSubmesh.BaseVertexLocation = boxVertexOffset; SubmeshGeometry pyramidSubmesh; pyramidSubmesh.IndexCount = pyramidIndices.size(); pyramidSubmesh.StartIndexLocation = pyramidIndexOffset; pyramidSubmesh.BaseVertexLocation = pyramidVertexOffset; std::vector<Vertex> totalVertex; totalVertex.insert(totalVertex.end(), std::begin(vertices), std::end(vertices)); totalVertex.insert(totalVertex.end(), std::begin(pyramidVertices), std::end(pyramidVertices)); std::vector<std::uint16_t> totalindices; totalindices.insert(totalindices.end(), std::begin(indices), std::end(indices)); totalindices.insert(totalindices.end(), std::begin(pyramidIndices), std::end(pyramidIndices)); const UINT vbByteSize = (UINT)totalVertex.size() * sizeof(Vertex); const UINT ibByteSize = (UINT)totalindices.size() * sizeof(std::uint16_t); mBoxGeo = std::make_unique<MeshGeometry>(); mBoxGeo->Name = "boxGeo"; ThrowIfFailed(D3DCreateBlob(vbByteSize, &mBoxGeo->VertexBufferCPU)); CopyMemory(mBoxGeo->VertexBufferCPU->GetBufferPointer(), totalVertex.data(), vbByteSize); ThrowIfFailed(D3DCreateBlob(ibByteSize, &mBoxGeo->IndexBufferCPU)); CopyMemory(mBoxGeo->IndexBufferCPU->GetBufferPointer(), totalindices.data(), ibByteSize); mBoxGeo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), totalVertex.data(), vbByteSize, mBoxGeo->VertexBufferUploader); mBoxGeo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), totalindices.data(), ibByteSize, mBoxGeo->IndexBufferUploader); mBoxGeo->VertexByteStride = sizeof(Vertex); mBoxGeo->VertexBufferByteSize = vbByteSize; mBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT; mBoxGeo->IndexBufferByteSize = ibByteSize; mBoxGeo->DrawArgs["box"] = boxSubmesh; mBoxGeo->DrawArgs["pyramid"] = pyramidSubmesh; }
7.2Draw方法修改
mCommandList->DrawIndexedInstanced(mBoxGeo->DrawArgs["box"].IndexCount, 1, mBoxGeo->DrawArgs["box"].StartIndexLocation, mBoxGeo->DrawArgs["box"].BaseVertexLocation, 0); mCommandList->DrawIndexedInstanced(mBoxGeo->DrawArgs["pyramid"].IndexCount, 1, mBoxGeo->DrawArgs["pyramid"].StartIndexLocation, mBoxGeo->DrawArgs["pyramid"].BaseVertexLocation, 0);
7.3 TODO 修改位置
8.修改“Box”演示程序,以线框模式来渲染立方体。
修改BuildPSO方法
auto crd = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); crd.FillMode = D3D12_FILL_MODE_WIREFRAME; psoDesc.RasterizerState = crd;
9.修改“Box”演示程序,首先禁用背面剔除(D3D12_CULL_MODE_NONE)并运行程序;随后,再代以正面剔除(D3D12_CULL_MODE_FRONT)试之。用线框模式输出程序的绘图效果,可便于我们观察不同剔除模式之间的绘制差别。
auto crd = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); //crd.FillMode = D3D12_FILL_MODE_WIREFRAME; //crd.CullMode = D3D12_CULL_MODE_NONE; crd.CullMode = D3D12_CULL_MODE_FRONT; psoDesc.RasterizerState = crd;
13.借助裁剪测试,剔除后台缓冲区中心宽为mClientWidth/2、高为mClientHeight/2这一矩形范围之外的所有像素。注意,要做到这一点,我们还需以D3D12_RECT结构体描述裁剪范围,并调用RSSetScissorRects方法对此进行设定。
mScissorRect这个参数已经声明在d3dApp.cpp
mScissorRect = { 0, 0, mClientWidth/2, mClientHeight/2 };
14.用像素着色器实现出可变色立方体的效果。在顶点着色器和像素着色器中,通过使用常量缓冲区以及简单的控制函数,使立方体的颜色随着时间的推移而平滑地发生变化。
14.1补充常量
struct ObjectConstants { XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4(); float time; };
14.2UpDate方法
ObjectConstants objConstants;
objConstants.time = gt.TotalTime();
14.3打开上面shader关于time的注释

浙公网安备 33010602011771号