赞
踩
https://github.com/jiabaodan/Direct12BookReadingNotes
下面的代码定义了2类顶点:
struct Vertex1
{
XMFLOAT3 Pos;
XMFLOAT4 Color;
};
struct Vertex2
{
XMFLOAT3 Pos;
XMFLOAT3 Normal;
XMFLOAT2 Tex0;
XMFLOAT2 Tex1;
};
当我们定义完一个顶点结构以后,我们需要提供一个描述来让Direct3D知道每个组件是什么作用,这个描述由Direct3D结构体D3D12_INPUT_LAYOUT_DESC通过输入布局描述(input layout description)的形式提供:
typedef struct D3D12_INPUT_LAYOUT_DESC
{
const D3D12_INPUT_ELEMENT_DESC *pInputElementDescs;
UINT NumElements;
} D3D12_INPUT_LAYOUT_DESC;
一个输入布局描述就是D3D12_INPUT_ELEMENT_DESC的数组,和数组的个数。
数组中每个元素用来描述顶点结构中对应的组件,D3D12_INPUT_ELEMENT_DESC结构体定义如下:
typedef struct D3D12_INPUT_ELEMENT_DESC
{
LPCSTR SemanticName;
UINT SemanticIndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D12_INPUT_CLASSIFICATION InputSlotClass;
UINT InstanceDataStepRate;
} D3D12_INPUT_ELEMENT_DESC;
DXGI_FORMAT_R32_FLOAT // 1D 32-bit float scalar
DXGI_FORMAT_R32G32_FLOAT // 2D 32-bit float vector
DXGI_FORMAT_R32G32B32_FLOAT // 3D 32-bit float vector
DXGI_FORMAT_R32G32B32A32_FLOAT // 4D 32-bit float vector
DXGI_FORMAT_R8_UINT // 1D 8-bit unsigned integer scalar
DXGI_FORMAT_R16G16_SINT // 2D 16-bit signed integer vector
DXGI_FORMAT_R32G32B32_UINT // 3D 32-bit unsigned integer vector
DXGI_FORMAT_R8G8B8A8_SINT // 4D 8-bit signed integer vector
DXGI_FORMAT_R8G8B8A8_UINT // 4D 8-bit unsigned integer vector
struct Vertex2
{
XMFLOAT3 Pos; // 0-byte offset
XMFLOAT3 Normal; // 12-byte offset
XMFLOAT2 Tex0; // 24-byte offset
XMFLOAT2 Tex1; // 32-byte offset
};
对于之前描述的两个顶点结构,其对应的输入布局描述如下:
D3D12_INPUT_ELEMENT_DESC desc1[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_PER_VERTEX_DATA, 0}
};
D3D12_INPUT_ELEMENT_DESC desc2[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_PER_VERTEX_DATA, 0},
{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_PER_VERTEX_DATA, 0}
{"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 32, D3D12_INPUT_PER_VERTEX_DATA, 0}
};
为了让GPU访问到顶点数组,我们需要将它们保存在一个叫缓冲(buffer)的GPU资源中(ID3D12Resource),用来保存顶点数据的缓冲叫做顶点缓冲。
如之前4.3.8,我们通过填写一个D3D12_RESOURCE_DESC结构体,创建一个ID3D12Resource对象来描述缓冲资源,然后调用ID3D12Device::CreateCommittedResource方法。Direct3D 12提供了一个C++封装的类CD3DX12_RESOURCE_DESC(继承自D3D12_RESOURCE_DESC),它提供了一个更加方便的构造方法:
static inline CD3DX12_RESOURCE_DESC Buffer(
UINT64 width,
D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE,
UINT64 alignment = 0 )
{
return CD3DX12_RESOURCE_DESC(
D3D12_RESOURCE_DIMENSION_BUFFER,
alignment, width, 1, 1, 1,
DXGI_FORMAT_UNKNOWN, 1, 0,
D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags );
}
with代表了缓冲中的字节数。
对于静态的几何体,我们将顶点缓冲放到默认堆中(default heap)((D3D12_HEAP_TYPE_DEFAULT)用以优化性能;为了创建实际的顶点缓冲资源,我们需要创建一个类型为D3D12_HEAP_TYPE_UPLOAD的上传缓冲(upload buffer)资源。
因为中间的上传缓冲需要在默认缓冲(default buffer)中初始化,所以我们在d3dUtil.h/.cpp中编写下面函数,用以避免重复代码:
Microsoft::WRL::ComPtr<ID3D12Resource> d3dUtil::CreateDefaultBuffer( ID3D12Device* device, ID3D12GraphicsCommandList* cmdList, const void* initData, UINT64 byteSize, Microsoft::WRL::ComPtr<ID3D12Resource>& uploadBuffer) { ComPtr<ID3D12Resource> defaultBuffer; // Create the actual default buffer resource. ThrowIfFailed(device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(byteSize), D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(defaultBuffer.GetAddressOf()))); // In order to copy CPU memory data into our default buffer, we need // to create an intermediate upload heap. ThrowIfFailed(device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(byteSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(uploadBuffer.GetAddressOf()))); // Describe the data we want to copy into the default buffer. D3D12_SUBRESOURCE_DATA subResourceData = {}; subResourceData.pData = initData; subResourceData.RowPitch = byteSize; subResourceData.SlicePitch = subResourceData.RowPitch; // Schedule to copy the data to the default buffer resource. // At a high level, the helper function UpdateSubresources // will copy the CPU memory into the intermediate upload heap. // Then, using ID3D12CommandList::CopySubresourceRegion, // the intermediate upload heap data will be copied to mBuffer. cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST)); UpdateSubresources<1>(cmdList, defaultBuffer.Get(), uploadBuffer.Get(), 0, 0, 1, &subResourceData); cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(defaultBuffer.D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ)); // Note: uploadBuffer has to be kept alive after the above function // calls because the command list has not been executed yet that // performs the actual copy. // The caller can Release the uploadBuffer after it knows the copy // has been executed. return defaultBuffer; }
D3D12_SUBRESOURCE_DATA结构体定义如下:
typedef struct D3D12_SUBRESOURCE_DATA
{
const void *pData;
LONG_PTR RowPitch;
LONG_PTR SlicePitch;
} D3D12_SUBRESOURCE_DATA;
下面的代码展示了该类使用的一个例子:
Vertex vertices[] = { { XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::White) }, { XMFLOAT3(-1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Black) }, { XMFLOAT3(+1.0f, +1.0f, -1.0f), XMFLOAT4(Colors::Red) }, { XMFLOAT3(+1.0f, -1.0f, -1.0f), XMFLOAT4(Colors::Green) }, { XMFLOAT3(-1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Blue) }, { XMFLOAT3(-1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Yellow) }, { XMFLOAT3(+1.0f, +1.0f, +1.0f), XMFLOAT4(Colors::Cyan) }, { XMFLOAT3(+1.0f, -1.0f, +1.0f), XMFLOAT4(Colors::Magenta) } }; const UINT64 vbByteSize = 8 * sizeof(Vertex); ComPtr<ID3D12Resource> VertexBufferGPU = nullptr; ComPtr<ID3D12Resource> VertexBufferUploader = nullptr; VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), vertices, vbByteSize, VertexBufferUploader);
顶点的定义如下:
struct Vertex
{
XMFLOAT3 Pos;
XMFLOAT4 Color;
};
为了绑定顶点缓冲到渲染管线,我们需要创建一个顶点缓冲描述(vertex buffer view)。和RTV(render target view)不同,我们不需要为顶点缓冲描述创建描述堆(descriptor heap),它可以通过D3D12_VERTEX_BUFFER_VIEW_DESC结构来表示:
typedef struct D3D12_VERTEX_BUFFER_VIEW
{
D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
UINT SizeInBytes;
UINT StrideInBytes;
} D3D12_VERTEX_BUFFER_VIEW;
当我们创建好一个顶点缓冲,并为它创建好描述后,我们可以把它绑定到渲染管线的一个输入槽,用以将顶点数据输入到输入阶段;这个过程可以使用下面函数完成:
void ID3D12GraphicsCommandList::IASetVertexBuffers(
UINT StartSlot,
UINT NumBuffers,
const D3D12_VERTEX_BUFFER_VIEW *pViews);
下面是一个调用的例子:
D3D12_VERTEX_BUFFER_VIEW vbv;
vbv.BufferLocation = VertexBufferGPU->GetGPUVirtualAddress();
vbv.StrideInBytes = sizeof(Vertex);
vbv.SizeInBytes = 8 * sizeof(Vertex);
D3D12_VERTEX_BUFFER_VIEW vertexBuffers[1] = { vbv };
mCommandList->IASetVertexBuffers(0, 1, vertexBuffers);
一个顶点缓冲将会保持在输入的槽,知道它被改变,所以你的代码应该类似下面:
ID3D12Resource* mVB1; // stores vertices of type Vertex1
ID3D12Resource* mVB2; // stores vertices of type Vertex2
D3D12_VERTEX_BUFFER_VIEW_DESC mVBView1; // view to mVB1
D3D12_VERTEX_BUFFER_VIEW_DESC mVBView2; // view to mVB2
/*…Create the vertex buffers and views…*/
mCommandList->IASetVertexBuffers(0, 1, &VBView1);
/* …draw objects using vertex buffer 1… */
mCommandList->IASetVertexBuffers(0, 1, &mVBView2);
/* …draw objects using vertex buffer 2… */
设置顶点缓冲到输入槽并没有开始绘制,它只是让顶点做好输入到渲染管线的准备,最终实际的渲染步骤是由ID3D12GraphicsCommandList::DrawInstanced方法完成:
void ID3D12CommandList::DrawInstanced(
UINT VertexCountPerInstance,
UINT InstanceCount,
UINT StartVertexLocation,
UINT StartInstanceLocation);
VertexCountPerInstance和StartVertexLocation参数定义了绘制那些顶点:
DrawInstanced方法中并没有指明拓扑结构,它由下面的方法中指定:
cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
和顶点一样,为了让GPU能访问到索引数据,我们需要创建一个索引缓冲和上传缓冲(Upload Buffer)。因为d3dUtil::CreateDefaultBuffer函数的数据是void*类型,所以可以直接使用它;为了绑定索引缓冲到渲染流水线,我们需要创建一个缓冲描述,和顶点一样,不需要描述堆;一个索引缓冲描述可以使用D3D12_INDEX_BUFFER_VIEW结构来表示:
typedef struct D3D12_INDEX_BUFFER_VIEW
{
D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
UINT SizeInBytes;
DXGI_FORMAT Format;
} D3D12_INDEX_BUFFER_VIEW;
和顶点一样,使用前需要绑定到流水线,可以使用ID3D12CommandList::SetIndexBuffer方法绑定到输入阶段:
std::uint16_t 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 }; const UINT ibByteSize = 36 * sizeof(std::uint16_t); ComPtr<ID3D12Resource> IndexBufferGPU = nullptr; ComPtr<ID3D12Resource> IndexBufferUploader = nullptr; IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(), mCommandList.Get(), indices), ibByteSize, Index
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。