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

image-20220315184610201image-20220315184900170image-20220315185018939image-20220315185143381

细分调很高的话会有一部分看不见了,尚未找到原因image-20220315185238091

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

image-20220316125456736

image-20220316123341882

/*
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);
这个我觉得有一点点不合理,不过对于平面来说是没问题的。这里暂时也是只是用到了正方体中。