D3D12渲染技术之渲染

我们在绘制对象时需要设置多个参数,例如绑定顶点和索引缓冲区,绑定对象常量,设置基元类型以及指定DrawIndexedInstanced参数。 当我们开始在场景中绘制更多对象时,创建一个存储绘制对象所需数据的轻量级结构会很有帮助。 这些数据因应用程序而异,因为我们添加了需要不同绘图数据的新功能。 我们将提交完整绘制所需的数据集称为渲染管道渲染项,我们的Render Item结构如下所示:

// Lightweight structure stores parameters to draw a shape. This will
// vary from app-to-app.
struct RenderItem
{
  RenderItem() = default;
 
  // World matrix of the shape that describes the object’s local space
  // relative to the world space, which defines the position, 
  // orientation, and scale of the object in the world.
  XMFLOAT4X4 World = MathHelper::Identity4x4();
 
  // Dirty flag indicating the object data has changed and we need 
  // to update the constant buffer. Because we have an object 
  // cbuffer for each FrameResource, we have to apply the
  // update to each FrameResource. Thus, when we modify obect data we
  // should set
  //  NumFramesDirty = gNumFrameResources so that each frame resource
  // gets the update.
  int NumFramesDirty = gNumFrameResources;
 
  // Index into GPU constant buffer corresponding to the ObjectCB 
  // for this render item.
  UINT ObjCBIndex = -1;
 
  // Geometry associated with this render-item. Note that multiple
  // render-items can share the same geometry.
  MeshGeometry* Geo = nullptr;
 
  // Primitive topology.
  D3D12_PRIMITIVE_TOPOLOGY PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
 
  // DrawIndexedInstanced parameters.
  UINT IndexCount = 0;
  UINT StartIndexLocation = 0;
  int BaseVertexLocation = 0;
};

我们的应用程序将根据需要绘制的方式维护渲染项目列表;也就是说,需要不同PSO的渲染项目将保存在不同的列表中。

std::vector<std::unique_ptr<RenderItem>> mAllRitems;
 
// Render items divided by PSO.
std::vector<RenderItem*> mOpaqueRitems;
std::vector<RenderItem*> mTransparentRitems;

传递常数

从上一篇博客中可以看出,我们在帧资源类中引入了一个新的常量缓冲区:

std::unique_ptr<UploadBuffer<PassConstants>> PassCB = nullptr;

在Demo中,此缓冲区存储在给定渲染过程中固定的常量数据,例如摄像机位置,视图和投影矩阵,以及有关屏幕(渲染目标)尺寸的信息; 它还包括游戏计时信息,这是在着色器程序中可以访问的有用数据。 请注意,我们的演示不一定会使用所有这些常量数据,但可以方便地使用,并且提供额外数据的成本很低。 例如,虽然我们现在不需要渲染目标大小,但是当我们实现一些后期处理效果时,将需要具有该信息。

cbuffer cbPass : register(b1)
{
  float4x4 gView;
  float4x4 gInvView;
  float4x4 gProj;
  float4x4 gInvProj;
  float4x4 gViewProj;
  float4x4 gInvViewProj;
  float3 gEyePosW;
  float cbPerObjectPad1;
  float2 gRenderTargetSize;
  float2 gInvRenderTargetSize;
  float gNearZ;
  float gFarZ;
  float gTotalTime;
  float gDeltaTime;
};

我们还修改了每个对象常量缓冲区,以仅存储与对象关联的常量。 到目前为止,我们与绘图对象关联的唯一常量数据是其世界矩阵:

cbuffer cbPerObject : register(b0)
{
   float4x4 gWorld; 
};

这些更改的想法是根据更新频率对常量进行分组, 每次通过常量只需要在每次渲染过程中更新一次,并且对象常量只需要在对象的世界矩阵发生变化时进行更改。 如果我们在场景中有一个静态对象,就像一棵树,我们只需要将其世界矩阵设置一次到一个常量缓冲区,然后再也不要更新常量缓冲区。 在我们的Demo中,我们实现了以下方法来处理每次传递和每个对象常量缓冲区的更新, Update方法中每帧调用一次这些方法。

void ShapesApp::UpdateObjectCBs(const GameTimer& gt)
{
  auto currObjectCB = mCurrFrameResource->ObjectCB.get();
  for(auto& e : mAllRitems)
  {
    // Only update the cbuffer data if the constants have changed. 
    // This needs to be tracked per frame resource.
    if(e->NumFramesDirty > 0)
    {
      XMMATRIX world = XMLoadFloat4x4(&e->World);
 
      ObjectConstants objConstants;
      XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
 
      currObjectCB->CopyData(e->ObjCBIndex, objConstants);
 
      // Next FrameResource need to be updated too.
      e->NumFramesDirty--;
    }
  }
}

 void ShapesApp::UpdateMainPassCB(const GameTimer& gt)
{
  XMMATRIX view = XMLoadFloat4x4(&mView);
  XMMATRIX proj = XMLoadFloat4x4(&mProj);
 
  XMMATRIX viewProj = XMMatrixMultiply(view, proj);
  XMMATRIX invView = XMMatrixInverse(&XMMatrixDeterminant(view), view);
  XMMATRIX invProj = XMMatrixInverse(&XMMatrixDeterminant(proj), proj);
  XMMATRIX invViewProj = XMMatrixInverse(&XMMatrixDeterminant(viewProj), viewProj);
 
  XMStoreFloat4x4(&mMainPassCB.View, XMMatrixTranspose(view));
  XMStoreFloat4x4(&mMainPassCB.InvView, XMMatrixTranspose(invView));

XMStoreFloat4x4(&mMainPassCB.Proj, XMMatrixTranspose(proj));
  XMStoreFloat4x4(&mMainPassCB.InvProj, XMMatrixTranspose(invProj));
  XMStoreFloat4x4(&mMainPassCB.ViewProj, XMMatrixTranspose(viewProj));
  XMStoreFloat4x4(&mMainPassCB.InvViewProj, XMMatrixTranspose(invViewProj));
  mMainPassCB.EyePosW = mEyePos;
  mMainPassCB.RenderTargetSize = XMFLOAT2((float)mClientWidth, (float)mClientHeight);
  mMainPassCB.InvRenderTargetSize = XMFLOAT2(1.0f / mClientWidth, 1.0f / mClientHeight);
  mMainPassCB.NearZ = 1.0f;
  mMainPassCB.FarZ = 1000.0f;
  mMainPassCB.TotalTime = gt.TotalTime();
  mMainPassCB.DeltaTime = gt.DeltaTime();
 
  auto currPassCB = mCurrFrameResource->PassCB.get();
  currPassCB->CopyData(0, mMainPassCB);
}

我们相应地更新顶点着色器以支持这些常量缓冲区更改:

VertexOut VS(VertexIn vin)
{
  VertexOut vout;
   
  // Transform to homogeneous clip space.
  float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
  vout.PosH = mul(posW, gViewProj);
   
  // Just pass vertex color into the pixel shader.
  vout.Color = vin.Color;
  
  return vout;
}

我们需要相应地更新根签名以获取两个描述符表(我们需要两个表,因为CBV将被设置为不同的频率 - 每次传递CBV仅需要在每个渲染过程中设置一次,而每个对象CBV需要 每个渲染项设置):

D3DX12_DESCRIPTOR_RANGE cbvTable0;
cbvTable0.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
 
CD3DX12_DESCRIPTOR_RANGE cbvTable1;
cbvTable1.Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1);
 
// Root parameter can be a table, root descriptor or root constants.
CD3DX12_ROOT_PARAMETER slotRootParameter[2];
 
// Create root CBVs.
slotRootParameter[0].InitAsDescriptorTable(1, &cbvTable0);
slotRootParameter[1].InitAsDescriptorTable(1, &cbvTable1);
 
// A root signature is an array of root parameters.
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(2, slotRootParameter, 0, nullptr, 
  D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

以上代码会在后面给出完整的,在这里只需要理解其能实现的具体作用即可。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页