D3D11顶点缓存
2024-05-15
58
0
在D3D11程序中,我们要绘制的每一个物体,它对应的所有顶点及索引信息,都需要存放在相应的缓存中。因此我们需要在初始化阶段为顶点和索引来创建相应的缓存。
顶点缓存和索引缓存的创建方式十分类似,即先定义缓存描述,再创建缓存数据(即顶点数组和索引数组),最后通过描述和缓存数据来创建缓存。
- 设置 D3D11_BUFFER_DESC 结构来描述buffer.
- 设置 D3D11_SUBRESOURCE_DATA 结构, 他指定了我们想要初始化缓冲区内容的数据.
- 调用 ID3D11Device::CreateBuffer 创建 vertex buffer.
顶点、索引缓冲使用同一个接口类型:ID3D11Buffer,因此其缓存描述也是一样的,该结构定义如下:
typedef struct D3D11_BUFFER_DESC {
UINT ByteWidth;
D3D11_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
UINT StructureByteStride;
} D3D11_BUFFER_DESC;
- ByteWidth为缓存大小,字节为单位;
- Usage,对于不变化的顶点、索引缓存,我们设为为D3D11_USAGE_VERTEX_IMMUTABLE,此外,针对CPU对缓存的读、写权限,这个成员有多个类型,比如D3D11_USAGE_DEFAULT(CPU不可读写)、D3D11_USAGE_DYNAMIC(CPU可读写),D3D11_USAGE_STAGING(CPU可读,即可拷贝);
- BindFlags,针对顶点缓存为D3D11_BIND_VERTEX_BUFFER,针对索引缓存为D3D11_BIND_INDEX_BUFFER;这个读与写同样是很慢的操作, GPU用渲染管线对顶点数据的处理进行优化, 但他更像是一条单行线, 没有考虑将传递的数据重新读回来. CPU 对数据的读写还可能会导致 GPU 的等待. CPU的读写可能很快, 但是如果可能, 我们应该尽量不设置任何FLAG, 让数据安静的呆在显存中, 让 GPU 来处理
- CPUAccessFlags,对于不允许CPU读、写的缓存,比如我们这次的例子,设为0,同时下一次成员也设为0;
- StructureByteStride 这个属性只对结构化的 buffer 有作用, 对于所有缓冲区都可以设置为0.
为了指定缓存数据,我们用到如下结构:
typedef struct D3D11_SUBRESOURCE_DATA {
const void *pSysMem;
UINT SysMemPitch;
UINT SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;
第一个成员即我们创建好的顶点、索引的数组;后面两个参数对于顶点、索引缓存用不到,设为0.
如下为创建顶点缓存的代码:
//
// A vertex with a position and texture coordinate
//
typedef struct _VERTEX
{
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
} VERTEX;
#define NUMVERTICES 6
BOOL CYUV::InitVertexBuffer()
{
VERTEX Vertices[NUMVERTICES] =
{
{DirectX::XMFLOAT3(-1.0f, -1.0f, 0),DirectX::XMFLOAT2(0.0f, 0.0f)},
{DirectX::XMFLOAT3(-1.0f, 1.0f, 0), DirectX::XMFLOAT2(0.0f, 1.0f)},
{DirectX::XMFLOAT3(1.0f, -1.0f, 0), DirectX::XMFLOAT2(1.0f, 0.0f)},
{DirectX::XMFLOAT3(1.0f, -1.0f, 0), DirectX::XMFLOAT2(1.0f, 0.0f)},
{DirectX::XMFLOAT3(-1.0f, 1.0f, 0), DirectX::XMFLOAT2(0.0f, 1.0f)},
{DirectX::XMFLOAT3(1.0f, 1.0f, 0), DirectX::XMFLOAT2(1.0f, 1.0f)},
};
// Set resources
UINT Stride = sizeof(VERTEX);
FLOAT blendFactor[4] = { 0.f, 0.f, 0.f, 0.f };
m_DeviceContext->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0);
m_DeviceContext->PSSetShader(m_PixelShader, nullptr, 0);
m_DeviceContext->PSSetShaderResources(0, 3, m_ShaderResViewYUV);
m_DeviceContext->PSSetSamplers(0, 1, &m_SamplerLinear);
m_DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Create vertex buffer
ID3D11Buffer* VertexBuffer = nullptr;
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES; //总的数据量大小
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices; //缓冲区指针
HRESULT hr = m_Device->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer);
if (FAILED(hr))
{
return FALSE;
}
UINT Offset = 0;
m_DeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &Stride, &Offset);
return TRUE;
}
顶点缓存绑定到输入槽
在 vertex buffer 创建之后, 我们还需要将他与 input slot 绑定, 这样才能作为渲染管线的输入. 这里使用 ID3D11DeviceContext::IASetVertexBuffers 方法来进行绑定.
void ID3D11DeviceContext::IASetVertexBuffers(
UINT StartSlot,
UINT NumBuffers,
ID3D11Buffer *const *ppVertexBuffers,
const UINT *pStrides,
const UINT *pOffsets);
- StartSlot: 开始绑定 vertex buffer 的 slot, 之前提到过这样的 slot 有16个, 序号从0到15.
- NumBuffers: 要绑定的 buffer 的数量.
- ppVertexBuffers: 一个指向 vertex buffer 数组头的指针.
- pStrides: 一个指向 stride 数组头的指针, 每一个 vertex buffer 对应一个 stride 值, 这个值表示 vertex buffer 中使用的元素的 byte 大小.
- pOffsets: 一个指向 offset 数组头的指针, 每一个 vertex buffer 对应一个 offset 值, 这个值表示从 vertex buffer 第一个元素到第一个要使用的元素的偏移值.
UINT Stride = sizeof(VERTEX);//每个顶点数据量大小
UINT Offset = 0;
m_DeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &Stride, &Offset);
这个函数看起来有些复杂是因为它支持传入多个 vertex buffer 到不同的 input slot 中, 但绝大多数情况我们都只会使用一个 input slot, 这个绑定与之前讨论过的 input layout 的绑定一样, 只有在手动修改之后才会发生改变.
渲染顶点
使用 Draw 方法来将顶点画出来. 接口的两个参数分别表示顶点的数量以及第一个顶点在 vertex buffer 中的位置.
void ID3D11DeviceContext::Draw(
UINT VertexCount,
UINT StartVertexLocation);
函数调用:
m_DeviceContext->Draw(NUMVERTICES, 0);// Draw textured quad onto render target