D3D11
+ -

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

0 篇笔记 写笔记

作者信息
站长漫谈
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!