c++ - 一个 Constant Buffer 可以用于多个 Object 吗?

标签 c++ directx directx-11

我是 Direct3D 11 的新手,我在理解如何基于每个对象更新常量(和其他缓冲区)时遇到了一些困难。我有一些简单的代码,我试图让两个四边形绘制到屏幕上,但在不同的位置。这是我用来绘制它们的代码。

// ------------------------------------------------------------------------------------------------------------------------------
void QuadShape::UpdateBuffers(ID3D11DeviceContext* pContext)
{
  // We need to know about our verts + set the constant buffers...
  // NOTE: We only really need to do this when the buffer data actually changes...
  XMMATRIX translate = XMMatrixTranspose(XMMatrixTranslation(X, Y, 0));
  XMStoreFloat4x4(&ConstData.World, translate);

  D3D11_MAPPED_SUBRESOURCE mappedResource;
    ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));

  pContext->Map(ConstBuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
  memcpy(mappedResource.pData, &ConstData, sizeof(ObjectConstBuffer));
  pContext->Unmap(ConstBuf, 0);

}

// ------------------------------------------------------------------------------------------------------------------------------
void QuadShape::Draw(ID3D11DeviceContext* pContext)
{
  UpdateBuffers(pContext);
  pContext->DrawIndexed(_VertCount, _StartVert, 0);
}

您可以看到我正在根据对象的当前 X/Y 位置计算平移矩阵,并将其映射到对象的常量缓冲区,表示为“ConstBuf”。我遇到的问题是所有四边形最终都在同一位置绘制,尽管我已经验证为每个四边形计算的矩阵确实不同,并且缓冲区确实是动态的。

我猜测正在发生的事情是映射资源只是被最后一个矩阵覆盖,但我认为 MAP_WRITE_DISCARD 应该避免这种情况。我很困惑,如何为每个对象使用不同的常量缓冲区,并让它们显示在不同的位置?

最佳答案

您应该按更新频率对常量缓冲区进行分组,因此如果您有一些数据会根据对象发生变化,请将其放在一个常量缓冲区中。如果您有其他数据(如投影矩阵)仅在调整窗口大小时发生变化,请将其放入另一个常量缓冲区。

因此,例如,定义两个这样的缓冲区:

struct CBPerObject
{
    XMMATRIX mWorld;    // world matrix
};

struct CBChangeOnResize
{
    XMMATRIX mProjection;     // projection matrix
};

然后创建常量缓冲区并在成员变量中保留对它们的引用:

CComPtr<ID3D11Buffer> m_pCBPerObject;        // dx11 constant buffer (per object)
CComPtr<ID3D11Buffer> m_pCBChangeOnResize;   // dx11 constant buffer (change on resize)

创建代码(为清楚起见省略了错误处理):

// create the constant buffers
D3D11_BUFFER_DESC pBuffDesc;
ZeroMemory(&pBuffDesc, sizeof(pBuffDesc));
pBuffDesc.Usage = D3D11_USAGE_DEFAULT;
pBuffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
pBuffDesc.CPUAccessFlags = 0;

// per object changes
pBuffDesc.ByteWidth = sizeof(CBPerObject);
m_pDevice->CreateBuffer(&pBuffDesc, nullptr, &m_pCBPerObject);

// on resize changes
pBuffDesc.ByteWidth = sizeof(CBChangeOnResize);
m_pDevice->CreateBuffer(&pBuffDesc, nullptr, &m_pCBChangeOnResize);

现在您可以在初始化期间将它们绑定(bind)一次(假设布局不会改变):

// constant buffers never change in shaders
pContext->VSSetConstantBuffers(0, 1, &m_pCBPerObject.p);
pContext->VSSetConstantBuffers(1, 1, &m_pCBChangeOnResize.p);

您可能还需要绑定(bind)到像素着色器,但请改用 PSSetConstantBuffers

现在,您只需在需要时更新常量缓冲区。例如,当窗口调整大小时:

void CMyClass::OnSize()
{
    // update projection matrix
    CBChangeOnResize cbBuffer;

    // calculate the projection matrix - for example:
    XMMATRIX mProjection = XMMatrixOrthographicOffCenterLH(fLeft, fRight, fBottom, 
                                                           fTop, 0.1f, 1000.0f);
    cbBuffer.mProjection = XMMatrixTranspose(mProjection);

    // update the constant buffer
    pContext->UpdateSubresource(m_pCBChangeOnResize, 0, nullptr, &cbBuffer, 0, 0);
}

同样,在绘制时,更新每个对象的世界矩阵:

void CMyClass::DrawScene()
{
    // draw the complete scene
    CBPerObject cbBuffer;

    // ... clear render target etc. 

    // for each object ... 
    {
        cbBuffer.mWorld = XMLoadFloat4x4(pObject->GetWorld());

        // update cb and draw object
        pContext->UpdateSubresource(m_pCBPerObject, 0, nullptr, &cbBuffer, 0, 0);
        pContext->DrawIndexed(6, 0, 0);
    }

    // ... etc.
}

因此除非布局发生变化,否则无需重新绑定(bind)常量缓冲区,并且您可以使用 UpdateSubresource 而不是 Map/Unmap 如上所示。

在您的顶点(像素)着色器中,根据它们绑定(bind)的插槽定义常量缓冲区:

cbuffer cbPerObject : register (b0)
{
    matrix mWorld;
};

cbuffer cbChangeOnResize : register (b1)
{
    matrix mProjection;
};

在语法中:register (b0)register (b1)中的b0b1是指提供给 VSSetConstantBuffers 的第一个参数。

关于c++ - 一个 Constant Buffer 可以用于多个 Object 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22184533/

相关文章:

c++ - IntelliSense:无法打开包含路径中的源文件

c# - Hook /覆盖 DirectX 游戏?

graphics - DirectX11 : Read stencil bit from compute shader

c++ - DirectX 项目模板在 Visual Studio 2017 中不可见

c++ - 如何从全屏 DirectX 11 将焦点转移到文件打开/保存对话框? (C++ VS2012)

c++ - 语法高亮 VS2012 C++ 和 OpenCV,库

c++ - 如何处理 Direct3D11 中的后备缓冲区?

c++ - 内存管理 : Returning a vector element and deleting(pop_back) it

c++ - DirectX 11 CreateSwapChain() 失败并出现错误 DXGI_ERROR_INVALID_CALL

c# - 以编程方式选择用于 DirectX 渲染的最佳显卡