DirectX 11 SDK文档(三)

DirectX 11 SDK文档(三)_第1张图片

总结

      在前面的教程中,我们创建了一个小型的,能够显示单颜色的窗口的Direct3D 11应用程序。在这个教程中,我们将扩展这个应用程序将一个三角形显示在屏幕上,我们将贯穿这个过程来创建关于三角形的数据结构。效果如上图。

三角形的元素

      一个三角形由三个点组成,也可以叫做顶点。用一个顶点数组来定义一个三角形,为了GPU能够渲染这个三角形,我们必须告诉GPU三角形的三个顶点的坐标。以2D为例,对我们来讲很容易就可以渲染一个像图1那样的三角形。我们要向GPU传送三个顶点(0,0),(0,1),(1,0),这样GPU有足够的信息来渲染我们想要的三角形。

DirectX 11 SDK文档(三)_第2张图片

      所以,我们现在知道我们必须传三个顶点坐标给GUP来渲染一个三角形。那我们要如何传这些信息给GPU呢?在Direct3D 11中,向顶点信息(如坐标)叫保存在一个缓冲区资源内。如果一个缓冲区用来存放顶点信息,那么它就叫做顶点缓冲区。我们必须创建一个足够大的顶点缓冲区来保存者三个顶点坐标。在Direct3D 11中,创建一个顶点缓冲区必须指定大小(以byte为单位)。我们知道这个缓冲区有足够的空间来存放这个顶点,但是每个顶点有多少byte呢?回答好这个问题之前,我们需要知道顶点格式。

Input Layout

      一个顶点有一个坐标信息。但通常不仅仅只有顶点信息,它也可能有其他的属性。比如:向量,多个的颜色,纹理坐标等等。顶点格式将定义这些属性在内存中如何存放:每个数据属性的用法,每个数据属性的大小,还有属性之间的排列。由于属性通常有不同的类型,类似于C中的结构体,一个顶点通常用一个结构体表示。可以方便地根据一个结构体的大小来获取顶点的大小。 

      在这个教程中,我们只使用到坐标信息,所以我们的顶点结构体只声明一个XMFLOAT3,XMFLOAT3由三个浮点数组成,它的典型应用就是3D内表示坐标。

      struct SimpleVertex{
             XMFLOAT3 Pos;  // Position
      };

      现在我们有一个结构体来表示我们的顶点。在应用程序中要保存好这些顶点信息。然而,当我们需要GPU渲染包含这些顶点的顶点缓冲区的时候,我们要将它填入一个内存块中。GPU也必须知道顶点格式,以便于从缓冲区中提取顶点信息。完成这个需要了解input layout。

      在Direct3D 11中,input layout是Direct3D对象,它能以一种GPU能够理解的方式来描述顶点结构体。每个顶点属性能够用D3D11_INPUT_ELEMENT_DESC结构体来描述。一个应用程序定义一个或多个的D3D11_INPUT_ELEMENT_DESC 结构体数组。使用这些数组来创建input layout对象来描述顶点。D3D11_INPUT_ELEMENT_DESC结构体细节如下:

SemanticName

Semantic Name是一个用来描述属性的字符串。这个字符串表示一个单词,这个单词可以使用C能够定义的任何格式。目前对于描述position属性的一个好的Semantic NamePOSITIONSemantic Name不区分大小写。

SemanticIndex

Semantic Index补充Semantic Name。一个顶点可能有多个属性是一样的。比如:有两组纹理坐标或者有两组颜色,用数字来代替Semantic Name的使用,比如:”COLOR0””COLOR1”。这两个元素可以共享一个Semantic Name—“COLOR”,使用01这两个不同的索引。

Format

元素使用Format定义了数据类型。目前,DXGI_FORMAT_R32G32B32_FLOAT数据格式有332位的浮点数,使这个元素有12字节大小。DXGI_FORMAT_R16G16B16A16_UINT416字节无符号整型数,使这个元素8字节大小。

InputSlot

根据以前的方法,一个Direct3D 11应用程序通过使用顶点缓冲区的方式把顶点数据传给GPUDirect3D 11在中,多种顶点缓冲区能够同时被传入GPU,确切的说是16种。每种顶点缓冲区将有一个数值在[0,15]绑定到Input slot。这个InputSlot告诉GPU可以从哪个顶点缓冲区获取这个元素。

 AlignedByteOffset

一个顶点保存在一个顶点缓冲区中,这个顶点缓冲区是一块连续的内存。AlignedByteOffset告诉GPU偏移量来获取这个元素。

 InputSlotClass

这个通常保存有D3D11_INPUT_PER_VERTEX_DATA。当一个应用程序使用Instancing时,它能够设置一个input layout的InputSlotClass到D3D11_INPUT_PER_INSTANCE_DATA与包含实例数据的顶点缓冲区一起工作,Instancing是一个超前的Direct3D话题,并且不会这里做深入讨论。对于我们的教程,我们将专门使用D3D11_INPUT_PER_VERTEX_DATA。

 InstanceDataStepRate

 这个用于Instancing,既然我们没有使用Instancing,InstanceDataStepRate没有被用到,所以必须设置为0

      现在我们能够定义我们的D3D11_INPUT_ELEMENT_DESC数组并且创建input layout:

      // Define the input layout
      D3D11_INPUT_ELEMENT_DESC layout[] ={
              { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 
      };
      UINT numElements = ARRAYSIZE(layout);

顶点格式

      在下一个教程,我们将解释这个技术对象和渲染器。现在,我们把注意力集中在创建Direct3D 11顶点格式对象。然而,我们将了解到顶点渲染器将紧密地跟顶点格式联系在一起。原因在于创建一个顶点格式对象需要顶点渲染器的输入签名,我们将使用D3DX11CompileFromFile函数所返回的ID3DBlob对象来检索二进制数据,该二进制数据代表顶点渲染器的输入签名。一旦我们获得这个数据,我们就能够调用ID3D11Device::CreateInputLayout()函数来创建顶点格式对象,和调用ID3D11DeviceContext::IASetInputLayout()来激活这个顶点格式。代码如下:

      // Create the input layout
      if( FAILED( g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
              pVSBlob->GetBufferSize(), &g_pVertexLayout ) ) )
                 return FALSE;
      // Set the input layout
      g_pImmediateContext->IASetInputLayout( g_pVertexLayout );

创建顶点缓冲区

      还有一件事在初始化时候要做的是创建顶点缓冲区保存顶点数据。在Direct3D 11中,为了创建一个顶点缓冲区,我们要填充两个结构体D3D11_BUFFER_DESC和D3D11_SUBRESOURCE_DATA,然后调用ID3D11Device::CreateBuffer()函数。D3D11_BUFFER_DESC描述要创建的顶点缓冲区对象。在顶点缓冲区创建的时候,D3D11_SUBRESOURCE_DATA描述在将要复制到顶点缓冲区的实际数据。顶点缓冲区在创建的时候初始化,所以之后我们就不要再初始化。将要复制到顶点缓冲区的数据是顶点数组,是3个SimpleVertex结构体的数组,顶点数组中坐标的选择是为了让三角形能显示在窗口的中央。顶点缓冲区创建之后,我们可以调用ID3D11DeviceContext::IASetVertexBuffers()将它绑定到device上。代码如下:

      // Create vertex buffer
      SimpleVertex vertices[] ={
              XMFLOAT3( 0.0f, 0.5f, 0.5f ),
              XMFLOAT3( 0.5f, -0.5f, 0.5f ),
              XMFLOAT3( -0.5f, -0.5f, 0.5f ),
      };
      D3D11_BUFFER_DESC bd;
      ZeroMemory( &bd, sizeof(bd) );
      bd.Usage = D3D11_USAGE_DEFAULT;
      bd.ByteWidth = sizeof( SimpleVertex ) * 3;
      bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
      bd.CPUAccessFlags = 0;
      bd.MiscFlags = 0;
      D3D11_SUBRESOURCE_DATA InitData;
      ZeroMemory( &InitData, sizeof(InitData) );
      InitData.pSysMem = vertices;
      if( FAILED( g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer ) ) )
                  return FALSE;
      // Set vertex buffer
      UINT stride = sizeof( SimpleVertex );
      UINT offset = 0;
      g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

Primitive Topology

      Primitive Topology是关于GPU如何获得要渲染三角形的3个顶点。我们上面讨论的都是如何让渲染一个简单三角形,应用程序需要传送这个3个顶点给GPU。所以,顶点缓冲区保存有3个顶点信息。那如果我们想要渲染两个三角形,该如何做呢?一种办法就是向GPU发送6个顶点。前3个顶点定义第一个三角形,后三个顶点定义第二个三角形,这个Topology叫做三角形列。三角形列是比较好理解的,当然它并不高效。这些例子能够成功地渲染三角形。目前为止图3a显示了一个矩形由两个三角形组成:ABC和CBD(按照惯例,以顺时针的顺序依次组成三角形列)。如果我们以三角形列的方式发送这两个三角形给GPU。我们的顶点应该这样定义:

      A B C C B D

注意:B和C在顶点缓冲区中出现了两次,因为他们分别被两个三角形使用。

DirectX 11 SDK文档(三)_第3张图片

      我们能够使顶点缓冲区更小,如果告诉GPU在渲染第二个三角形的时候,重复使用第一个三角形的最后两个顶点,然后再加上接下来的一个顶点组成第二个三角形。Direct3D支持这种格式,它叫做三角形带。当渲染一个三角形带时,第一个三角形由顶点数组的前三个顶点定义,那个接下来的三角形由前一个三角形的最后两个顶点和接下来的一个顶点定义。那么顶点应该这样定义:

      A B C D

      前三个顶点,即A B C定义第一个三角形。第二个三角形由B C(第一个三角形的最后两个顶点)加上D来定义。那个通过使用三角带,我们的顶点缓冲区从之前的6个顶点缩小为现在的4个顶点。

      同理如图3b,使用三角列的话,顶点定义如下:ABC CBD CDE

      如果使用三角带的话,顶点定义如下:A B C D E

      你可能会注意到三角带的例子中,第二个三角形是由B C D来定义,这三个顶点的排列不是顺时针方向。使用三角形带时这是很正常的现象。这种现象只出现在第二个三角形,第四个三角形,第六个三角形,第八个三角形等等。这就确保了每个三角形的顶点都能够绕顺时针方向(顺时针,在这个例子中)。除了三角形列和三角形带,还有其他一些Direct3D支持的Primitive Topology,在这个教程上不做讨论。

      在我们的代码里,我们只需要一个三角形,所以我们用三角形列还是三角形带都可以。但是它必须定义,所以我们使用三角形列。

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

渲染三角形

      最后的代码就是如何渲染这个三角形。我们已经创建了两个渲染器用于渲染,顶点渲染器和像素渲染器。顶点渲染器负责将三角形的各个顶点变换到正确的位置。像素渲染器负责三角形内部的最后输出颜色的计算。这个将在下一个教程详细讲解。为了使用这两个渲染器,我们必须分别调用ID3D11DeviceContext::VSSetShader()函数和ID3D11DeviceContext::PSSetShader()函数。最后我们要做的就是调用ID3D11DeviceContext::Draw()函数,该函数命令GPU使用当前的顶点缓冲区,当前的顶点格式,当前的primitive topology进行渲染。Draw()的第一个参数要传给GPU的顶点个数。第二个参数表示顶点缓冲区的起始索引。因为我们要渲染一个三角形,并且我们从顶点缓冲区的0位置开始。我们分别用3和0来填充这两个参数。代码如下:

      // Render a triangle
      g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
      g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
      g_pImmediateContext->Draw( 3, 0 );

你可能感兴趣的:(layout,文档,input,Primitive,Direct3D,Semantic)