DX12 第七章习题
DX12-7th
7th Capture
something before 7
记忆被清空了,有点搞不懂 worldViewProj 是怎么产生的了,还有 cb 是怎么绑定的?
world 就是物体在世界空间的位置
view 是物体从世界空间,变换到摄像机空间(视察空间)的矩阵
Proj 则是物体在视察空间中映射到二维平面上的矩阵
constant buffer 的绑定过程,以下是需要绑定的结构体
// 需要绑定的 CB
struct ObjectConstants
{
DirectX::XMFLOAT4X4 World = MathHelper::Identity4x4();
};
struct PassConstants
{
DirectX::XMFLOAT4X4 View = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 InvView = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 Proj = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 InvProj = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 ViewProj = MathHelper::Identity4x4();
DirectX::XMFLOAT4X4 InvViewProj = MathHelper::Identity4x4();
DirectX::XMFLOAT3 EyePosW = { 0.0f, 0.0f, 0.0f };
float cbPerObjectPad1 = 0.0f;
DirectX::XMFLOAT2 RenderTargetSize = { 0.0f, 0.0f };
DirectX::XMFLOAT2 InvRenderTargetSize = { 0.0f, 0.0f };
float NearZ = 0.0f;
float FarZ = 0.0f;
float TotalTime = 0.0f;
float DeltaTime = 0.0f;
};
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
};
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
};
struct FrameResource
{
public:
FrameResource(ID3D12Device* device, UINT passCount, UINT objectCount);
FrameResource(const FrameResource& rhs) = delete;
FrameResource& operator=(const FrameResource& rhs) = delete;
~FrameResource();
// We cannot reset the allocator until the GPU is done processing the commands.
// So each frame needs their own allocator.
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> CmdListAlloc;
// We cannot update a cbuffer until the GPU is done processing the commands
// that reference it. So each frame needs their own cbuffers.
std::unique_ptr<UploadBuffer<PassConstants>> PassCB = nullptr;
std::unique_ptr<UploadBuffer<ObjectConstants>> ObjectCB = nullptr;
// Fence value to mark commands up to this fence point. This lets us
// check if these frame resources are still in use by the GPU.
UINT64 Fence = 0;
};
/*
第一步:创建根签名,把一个含有 cbv 的描述符表 绑定到常量缓冲区寄存器 0,总感觉这个绑定过程有冗余
*/
void ShapesApp::BuildRootSignature()
{
CD3DX12_DESCRIPTOR_RANGE cbvTable0;
cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
//...
// Create root CBVs.
slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);
//...
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
//...
md3dDevice->CreateRootSignature(...);
//...
}
/*
第二步:创建了帧资源,在帧资源中存储着 PassCB,ObjectCB 的指针
简单初始化了 mFrameResources
*/
void ShapesApp::BuildFrameResources()
{
for(int i = 0; i < gNumFrameResources; ++i)
{
mFrameResources.push_back(std::make_unique<FrameResource>(md3dDevice.Get(),
1, (UINT)mAllRitems.size()));
}
}
/*
第三步:创建了堆描述符,为了将常量缓冲区绑定到渲染流水线上,但是此时还没有绑定
创建了 mCbvHeap,以及一些数据 mPassCbvOffset
*/
void ShapesApp::BuildDescriptorHeaps()
{
UINT objCount = (UINT)mOpaqueRitems.size();
// Need a CBV descriptor for each object for each frame resource,
// +1 for the perPass CBV for each frame resource.
UINT numDescriptors = (objCount+1) * gNumFrameResources;
// Save an offset to the start of the pass CBVs. These are the last 3 descriptors.
mPassCbvOffset = objCount * gNumFrameResources;
D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
cbvHeapDesc.NumDescriptors = numDescriptors;
cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
cbvHeapDesc.NodeMask = 0;
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc,
IID_PPV_ARGS(&mCbvHeap)));
}
/*
第四步:对每一个帧资源,使用 Resource() 获得 ID3D12Resource* objectCB,然后通过,GetGPUVirtualAddress() 循环获得D3D12_GPU_VIRTUAL_ADDRESS cbAddress,修改到对应数据的偏移位置处,
UploadBuffer::Resource(...)->ID3D12Resource*
ID3D12Resource::GetGPUVirtualAddress(...)->D3D12_GPU_VIRTUAL_ADDRESS
再把 cbAddress 偏移到不同物体的上,对每一个物体(用 heapIndex 指明是哪个物体),获取 handle 再配置 cbvDesc(其中包含有 cbAddress 确保偏移) 创建 cbv
CD3DX12_CPU_DESCRIPTOR_HANDLE->CD3DX12_CPU_DESCRIPTOR_HANDLE
ID3D12Device->CreateConstantBufferView(&cbvDesc, handle)
*/
void ShapesApp::BuildConstantBufferViews()
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
UINT objCount = (UINT)mOpaqueRitems.size();
// Need a CBV descriptor for each object for each frame resource.
for(int frameIndex = 0; frameIndex < gNumFrameResources; ++frameIndex)
{
auto objectCB = mFrameResources[frameIndex]->ObjectCB->Resource();
for(UINT i = 0; i < objCount; ++i)
{
D3D12_GPU_VIRTUAL_ADDRESS cbAddress = objectCB->GetGPUVirtualAddress();
// Offset to the ith object constant buffer in the buffer.
cbAddress += i*objCBByteSize;
// Offset to the object cbv in the descriptor heap.
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);
}
}
//...
//...
}
/*
第五步:还需要最终的绑定,需要 gpu 去执行指令(不知道可不可以这么理解)
*/
void ShapesApp::DrawRenderItems(ID3D12GraphicsCommandList* cmdList, const std::vector<RenderItem*>& ritems)
{
UINT objCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
auto objectCB = mCurrFrameResource->ObjectCB->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);
// Offset to the CBV in the descriptor heap for this object and for this frame resource.
UINT cbvIndex = mCurrFrameResourceIndex*(UINT)mOpaqueRitems.size() + ri->ObjCBIndex;
auto cbvHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(mCbvHeap->GetGPUDescriptorHandleForHeapStart());
cbvHandle.Offset(cbvIndex, mCbvSrvUavDescriptorSize);
cmdList->SetGraphicsRootDescriptorTable(0, cbvHandle);
cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
}
}
// 上面的顺序也能替换一下,BuildRootSignature() 放后面一点也没事。
// 最后保持映射状态,直接 CopyData 就好了,不断更新。
7.9.1
Modify the “Shape” demo to use GeometryGenerator::CreateGeosphere instead of GeometryGenerator::CreateSphere. Try with 0, 1, 2, and 3 subdivision levels
// GeometryGenerator::MeshData GeometryGenerator::CreateGeosphere(float radius, uint32 numSubdivisions)
GeometryGenerator::MeshData sphere = geoGen.CreateGeosphere(0.5f, 0);
//以下分别为0,1,2,3
细分调很高的话会有一部分看不见了,尚未找到原因
7.9.2
Modify the “Shapes” demo to use sixteen root constants to set the per-object world matrix instead of a descriptor table
我就说我看完了这一章有些东西真的只是看看过,知识没有进脑子,这练习多是一件美事啊啊哈哈哈哈…
服了,他的坐标系,i,j 和我理解的不太一样,画出来就异常的不对劲。
/*
前面的任务就是删掉第一个 CB,CBV 等。修改根签名。看了别人的做法,特意修改了 ObjectCB,其实没有必要,因为是常量,不需要每次去修改,赋值
gWorld,按照我的理解,应该是竖向的填充。
*/
float srcData[4][4] = {
ri->World(0, 0), ri->World(1, 0), ri->World(2, 0), ri->World(3, 0),
ri->World(0, 1), ri->World(1, 1), ri->World(2, 1), ri->World(3, 1),
ri->World(0, 2), ri->World(1, 2), ri->World(2, 2), ri->World(3, 2),
ri->World(0, 3), ri->World(1, 3), ri->World(2, 3), ri->World(3, 3), };
cmdList->SetGraphicsRoot32BitConstants(0, 16, srcData, 0);
//color.hlsl
/*
这个没什么好改的
*/
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
};
// 花了几个小时,就因为这个顺序。。。还得是动调
7.9.3
/*
Summary: 从一定格式的文件中读取顶点和索引
Parameters:
path: 路径
numSubdivisions: 细分次数,似乎这个图像不太能继续细分了
*/
MeshData CreateFromFile(char* path, uint32 numSubdivisions=0);
GeometryGenerator::MeshData GeometryGenerator::CreateFromFile(char* path, uint32 numSubdivisions)
{
MeshData meshData;
float pos[3];
float normal[3];
uint32_t tlist[3];
char mystr[30];
uint64_t vertexCount;
uint64_t triangleCount;
std::ifstream infile(path, std::ios::in);
if (infile.is_open())
{
infile >> mystr >> vertexCount;
infile >> mystr >> triangleCount;
infile.getline(mystr, 30);
infile.getline(mystr, 30);
infile.getline(mystr, 30);
while (vertexCount--)
{
infile >> pos[0]; infile >> pos[1]; infile >> pos[2];
infile >> normal[0]; infile >> normal[1]; infile >> normal[2];
Vertex vertex = Vertex(
pos[0], pos[1], pos[2],
normal[0], normal[1], normal[2],
1.0f, 0.0f, 0.0f,
0.0f, 0.0f);
meshData.Vertices.push_back(vertex);
}
infile.getline(mystr, 30);
infile.getline(mystr, 30);
infile.getline(mystr, 30);
infile.getline(mystr, 30);
while (triangleCount--)
{
infile >> tlist[0] >> tlist[1] >> tlist[2];
meshData.Indices32.push_back(tlist[0]);
meshData.Indices32.push_back(tlist[1]);
meshData.Indices32.push_back(tlist[2]);
}
infile.close();
}
for (auto i = 0; i < numSubdivisions; i++) {
Subdivide(meshData);
}
return meshData;
}
然后要修改一下 BuildShapeGeometry
GeometryGenerator::MeshData skull = geoGen.CreateFromFile("C:\\Users\\orz10\\ROOT\\directx12\\d3d12book\\Chapter 7 Drawing in Direct3D Part II\\Shapes\\skull.txt");
//...
UINT skullIndexOffset = cylinderIndexOffset + (UINT)cylinder.Indices32.size();
//...
SubmeshGeometry skullSubmesh;
skullSubmesh.IndexCount = (UINT)skull.Indices32.size();
skullSubmesh.StartIndexLocation = skullIndexOffset;
skullSubmesh.BaseVertexLocation = skullVertexOffset;
//...
auto totalVertexCount =
box.Vertices.size() +
grid.Vertices.size() +
sphere.Vertices.size() +
cylinder.Vertices.size() +
skull.Vertices.size();
//来一手黑色骨头,我发现白色的更带劲
for (size_t i = 0; i < skull.Vertices.size(); ++i, ++k)
{
vertices[k].Pos = skull.Vertices[i].Position;
vertices[k].Color = XMFLOAT4(DirectX::Colors::Black);
}
//...
geo->DrawArgs["skull"] = skullSubmesh;
最后把 BuildRenderItems 改一下就好了,这个拷贝了数据给要画的 mOpaqueRitems,算是最后一步了。
auto skullRitem = std::make_unique<RenderItem>();
XMStoreFloat4x4(&skullRitem->World, XMMatrixScaling(1.0f, 1.0f, 1.0f) * XMMatrixTranslation(0.0f, -1.5f, 0.0f));
skullRitem->ObjCBIndex = 0;
skullRitem->Geo = mGeometries["shapeGeo"].get();
skullRitem->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
skullRitem->IndexCount = skullRitem->Geo->DrawArgs["skull"].IndexCount;
skullRitem->StartIndexLocation = skullRitem->Geo->DrawArgs["skull"].StartIndexLocation;
skullRitem->BaseVertexLocation = skullRitem->Geo->DrawArgs["skull"].BaseVertexLocation;
mAllRitems.push_back(std::move(skullRitem));
7.9.X
关于这个曲面细分我有点迷糊,不知道为什么有些显示不了了
// 摘抄
void GeometryGenerator::Subdivide(MeshData& meshData)
{
// Save a copy of the input geometry.
MeshData inputCopy = meshData;
meshData.Vertices.resize(0);
meshData.Indices32.resize(0);
// v1
// *
// / \
// / \
// m0*-----*m1
// / \ / \
// / \ / \
// *-----*-----*
// v0 m2 v2
uint32 numTris = (uint32)inputCopy.Indices32.size()/3;
for(uint32 i = 0; i < numTris; ++i)
{
Vertex v0 = inputCopy.Vertices[ inputCopy.Indices32[i*3+0] ];
Vertex v1 = inputCopy.Vertices[ inputCopy.Indices32[i*3+1] ];
Vertex v2 = inputCopy.Vertices[ inputCopy.Indices32[i*3+2] ];
//
// Generate the midpoints.
//
Vertex m0 = MidPoint(v0, v1);
Vertex m1 = MidPoint(v1, v2);
Vertex m2 = MidPoint(v0, v2);
//
// Add new geometry.
//
meshData.Vertices.push_back(v0); // 0
meshData.Vertices.push_back(v1); // 1
meshData.Vertices.push_back(v2); // 2
meshData.Vertices.push_back(m0); // 3
meshData.Vertices.push_back(m1); // 4
meshData.Vertices.push_back(m2); // 5
meshData.Indices32.push_back(i*6+0);
meshData.Indices32.push_back(i*6+3);
meshData.Indices32.push_back(i*6+5);
meshData.Indices32.push_back(i*6+3);
meshData.Indices32.push_back(i*6+4);
meshData.Indices32.push_back(i*6+5);
meshData.Indices32.push_back(i*6+5);
meshData.Indices32.push_back(i*6+4);
meshData.Indices32.push_back(i*6+2);
meshData.Indices32.push_back(i*6+3);
meshData.Indices32.push_back(i*6+1);
meshData.Indices32.push_back(i*6+4);
}
}
把两个点的所有属性平分了。最为关键的是 MidPoint 中存在 XMVECTOR pos = 0.5f*(p0 + p1);
这个我觉得有一点点不合理,不过对于平面来说是没问题的。这里暂时也是只是用到了正方体中。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 R0gerThat!
评论