对于 DirectX 11,我不能在每个渲染()中计算缓冲区吗?

Posted

技术标签:

【中文标题】对于 DirectX 11,我不能在每个渲染()中计算缓冲区吗?【英文标题】:What can I do not to calculate buffers in every render(), for DirectX 11? 【发布时间】:2012-07-04 05:17:17 【问题描述】:

我正在编写自己的“引擎”类,以便更轻松地使用和学习 DirectX 11。该“引擎”有一个要渲染的对象列表。其中一些是“Primitives”(自己的 Box 类)。

问题在于 Box::render() 每次函数调用都会创建一个缓冲区。

我可以用它改变一些东西而不是在每次函数调用时计算它们吗?

也许我应该在构造函数中只计算一次 g_pIndexBuffer 并为每个 Box 保留它?但是 g_pVertexBuffer 和 g_pConstantBuffer 呢?

我不仅要盒子,而且更笼统 - 当我想要绘制其他 Primitives 或网格时,情况会改变吗? (对于 Box,我想我可以计算一次 g_pVertexBuffer,而不仅仅是用矩阵缩放对象)。

现在的代码是这样的(我这里跳过了部分代码,代码基于 DirectX SDK 的教程):

Box::render(
    XMFLOAT4X4 &viewM, 
    XMFLOAT4X4 &projectionM, 
    ID3D11Buffer* g_pConstantBuffer, ID3D11DeviceContext* g_pImmediateContext,
    ID3D11VertexShader* g_pVertexShader,
    ID3D11PixelShader* g_pPixelShader,
    ID3D11Device * g_pd3dDevice,
    ID3D11Buffer* g_pIndexBuffer)

    ...
    // Create vertex buffer
    SimpleVertex vertices[] =

     XMFLOAT3( -1.0f, 1.0f, -1.0f ), XMFLOAT4( 0.0f, 0.0f, 1.0f, 1.0f ) ,
     XMFLOAT3( 1.0f, 1.0f, -1.0f ), XMFLOAT4( 0.0f, 1.0f, 0.0f, 1.0f ) ,
     XMFLOAT3( 1.0f, 1.0f, 1.0f ), XMFLOAT4( 0.0f, 1.0f, 1.0f, 1.0f ) ,
     XMFLOAT3( -1.0f, 1.0f, 1.0f ), XMFLOAT4( 1.0f, 0.0f, 0.0f, 1.0f ) ,
     XMFLOAT3( -1.0f, -1.0f, -1.0f ), XMFLOAT4( 1.0f, 0.0f, 1.0f, 1.0f ) ,
     XMFLOAT3( 1.0f, -1.0f, -1.0f ), XMFLOAT4( 1.0f, 1.0f, 0.0f, 1.0f ) ,
     XMFLOAT3( 1.0f, -1.0f, 1.0f ), XMFLOAT4( 1.0f, 1.0f, 1.0f, 1.0f ) ,
     XMFLOAT3( -1.0f, -1.0f, 1.0f ), XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f ) ,
;

    D3D11_BUFFER_DESC bd;
    ZeroMemory( &bd, sizeof(bd) );
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( SimpleVertex ) * 8;
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    D3D11_SUBRESOURCE_DATA InitData;
    ZeroMemory( &InitData, sizeof(InitData) );
    InitData.pSysMem = vertices;
ID3D11Buffer* g_pVertexBuffer;
HRESULT hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );

    // Set vertex buffer
UINT stride = sizeof( SimpleVertex );
    UINT offset = 0;
    g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset);

    WORD indices[] =
    
    3,1,0, 2,1,3,
    0,5,4, 1,5,0,
    3,4,7, 0,4,3,
    1,6,5, 2,6,1,
    2,7,6, 3,7,2,
    6,4,5, 7,4,6,
    ;


    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( WORD ) * 36; // 36 vertices needed for 12 triangles in a triangle list
    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
    InitData.pSysMem = indices;
    hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pIndexBuffer );


    // Set index buffer
    g_pImmediateContext->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );

    // Set primitive topology
    g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

// Create the constant buffer
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(ConstantBuffer2);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
    hr = g_pd3dDevice->CreateBuffer( &bd, NULL, &g_pConstantBuffer );

    ...
    XMMATRIX mSpin = XMMatrixRotationZ( a );
    XMMATRIX mOrbit = XMMatrixRotationY( b );
    XMMATRIX mTranslate = XMMatrixTranslation( c, d, e );
    XMMATRIX mScale = XMMatrixScaling( f, g, h );

    XMMATRIX g_World = mScale * mSpin * mTranslate * mOrbit;

    ConstantBuffer2 cb1;
    cb1.mWorld = XMMatrixTranspose( g_World );

    XMMATRIX g_View = XMLoadFloat4x4(&viewM);
    XMMATRIX g_Projection = XMLoadFloat4x4(&projectionM);
    cb1.mView = XMMatrixTranspose( g_View );
    cb1.mProjection = XMMatrixTranspose( g_Projection );
    g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb1, 0, 0 );

    g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
    g_pImmediateContext->VSSetConstantBuffers( 0, 1, &g_pConstantBuffer );
    g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
    g_pImmediateContext->DrawIndexed( 36, 0, 0 );

【问题讨论】:

【参考方案1】:

您的问题的答案取决于缓冲区的类型。当然,还有你正在创建的应用程序的类型。

顶点和索引缓冲区应该只创建一次并尽可能频繁地重复使用。创建的一个好地方是代表您的对象的类的构造函数。此外,如果对象之间存在共享几何,您可以考虑创建一个几何类(在 DX 9 之前的版本中类似于 Mesh)。然后在几何类的构造函数中创建缓冲区一次,并从对象中引用适当的几何实例。

当然,在某些情况下,尤其是顶点缓冲区可能会在整个应用程序中发生变化。但即便如此,您也应该在开始时创建它们一次,然后在必要时重写它们。但这应该尽可能少地发生。可以在适当的着色器中对顶点缓冲区进行许多更改。

对于常量缓冲区,这有点棘手。常量缓冲区应该被划分为变化的数据:

从不 每帧 每帧和每对象。 可能还有其他必要的

这意味着应该在哪里创建职位。从不改变缓冲区应该在应用程序开始时创建一次(就像顶点和索引缓冲区一样)。每帧缓冲区应在新帧开始时创建,每对象缓冲区应在渲染对象时创建。

请记住,缓冲区可以存在而不被设置为当前缓冲区。下面是缓冲区生命周期的样子:

创建顶点、索引和永不改变的常量缓冲区 对于每一帧 创建每帧常量缓冲区 对于每个对象 为每个对象创建常量缓冲区 设置顶点、索引和所有常量缓冲区 渲染对象

随着您的进步,本指南或多或少会发生一些变化。例如,您可以按着色器、几何体等对对象进行排序,并相应地设置缓冲区。

【讨论】:

感谢您提供非常复杂的答案。从中,我明白我应该只有一个顶点和一个索引缓冲区?我猜是因为您告诉在渲染循环之前创建它,但是我的对象可以在程序工作期间添加,而不仅仅是在第一次渲染之前。 但即便如此,新对象也可能基于某些现有几何图形。如果不是,那没问题,如果您在创建对象时创建顶点缓冲区。如果所有对象的几何形状都相同(除了一些变换),那么确实只需要一个顶点/索引缓冲区。 如果几何体是同一类型的,那么每种几何体只需要一个缓冲区。那么如果几何体的数据(顶点位置、法线...)在每一帧中发生变化,就可以使用map/unmap方法。

以上是关于对于 DirectX 11,我不能在每个渲染()中计算缓冲区吗?的主要内容,如果未能解决你的问题,请参考以下文章

DirectX* 11 多线程渲染的性能方法和实践

DirectX 11 渲染到特定区域

DirectX11 2 窗口渲染

DirectX11第二篇 DirectX11渲染管线(2016.05.09更新)

DirectX11第二篇 DirectX11渲染管线(2016.05.09更新)

C++小项目:directx11图形程序:总结