C++Directx11开发笔记三:绘制图形

在前面我们讲过了如何初始化D3D11Device设备初始化等等,这里所讲的绘制图形将在上一篇文章的项目里进行扩展,在屏幕中绘制图形。在3D的呈现中最小的单位为三角形,无论我们看到的是多么大或多么小的,都是有一个或很多个三角形通过各种方向,角度构成的,当然这会涉及到很多数学中的几何学问题,最悲剧的就是我在大学里却没学好代数以及几何学,有学也忘记了。不过Directx SDK中以及为我们解决了很多几何上的问题,通过他们的方法就可以得到结果,说了这么多目的就是我告诉大家,要掌握高阶运用,必然要学会基础知识,所以我们这里就来学习一下如何在屏幕上绘制一个三角形,并涂上颜色。

 

一个三角形由三个点组成,也可以说坐标。在坐标系里,三个不同的点就可以组成一个唯一的三角形,当然也就是唯一的面,我想3D图形是由很多个面组成的,这也就是最小单位为三角形的原因。为了能够让GPU(就是显卡中的CPU,简单的认为一下,(*^__^*) )呈现三角形,我们必须告诉他三个点的坐标,那样他才能够在屏幕中显示出来。例如:在2D中,我们要在屏幕中画出如下的图示的三角形,就必须告诉GPU他们三个顶点(0,0),(0,1),(1,0)的坐标,那样GPU才能够画出他们。

C++Directx11开发笔记三:绘制图形_第1张图片

我们已经知道要把顶点告诉GPU,但是如何告诉他们呢?在Direct3D 11中,顶点信息如三角形三个顶点的坐标是存储在一个缓存资源中的,叫做顶点缓存(Vertex Buffer)。我们必须创建一个足够大的顶点缓存,让他能够承载三角形的三个顶点坐标信息。

 

 INPUT LAYOUT

一个顶点不只包含一个坐标,还可能包含一个或多个颜色值,纹理坐标等等,而Input Layout就是定义这些信息如何在内存中存储:不同数据类型将会有不同的大小, 当然不同的大小也决定着不同的存储顺序。和C语言很像,一个顶点一般使用一个结构来定义。在这里,我们只需要定义一个三角形顶点的坐标,所以我们只要使用XMFLOAT3来定义一个坐标,具体代码如下:

//  3D Vector; 32 bit floating point components
typedef  struct  _XMFLOAT3
{
    FLOAT x;
    FLOAT y;
    FLOAT z;

#ifdef __cplusplus

    _XMFLOAT3() {};
    _XMFLOAT3(FLOAT _x, FLOAT _y, FLOAT _z) : x(_x), y(_y), z(_z) {};
    _XMFLOAT3(CONST FLOAT 
* pArray);

    _XMFLOAT3
&   operator =  (CONST _XMFLOAT3 &  Float3);

#endif   //  __cplusplus

} XMFLOAT3;

//  以上是XMFLOAT3的结构信息,在3D中坐标的结构

struct  SimpleVertex
{
    XMFLOAT3 Pos;  
//  Position
};

 

 我们定义了一个SimpleVertex结构,就是为了存储三角形的顶点坐标,为了能够让GPU了解并且能够在内存中获得相关信息,我们就必须使用到Input Layout。在Direct 3D 11中,一个Input Layout被定义成能够让GPU识别的结构信息,每一个顶点属性可以使用一个叫D3D11_INPUT_ELEMENT_DESC结构来进行描述。不同的顶点信息可以通过定义一个数组来解决这个问题,那样每一个顶点都能够进行描述,下面让我们来了解一下这个结构的具体属性。

typedef struct D3D11_INPUT_ELEMENT_DESC     {
    LPCSTR SemanticName;
    UINT SemanticIndex;
    DXGI_FORMAT Format;
    UINT InputSlot;
    UINT AlignedByteOffset;
    D3D11_INPUT_CLASSIFICATION InputSlotClass;
    UINT InstanceDataStepRate;
    }     D3D11_INPUT_ELEMENT_DESC;

 属性说明:

  •   SemanticName:用来描述目的或名称的字符,只要任何符合C语言结构的字符串都可以使用,并且忽略大小写,比如用于描述顶点坐标可以使用“POSITION”字符串。
  • SemanticIndex:这个用来描述索引,当SemanticName相同的情况下就可以使用索引来描述到底哪个才是当前需要的。因为一个顶点可能包含多个颜色,或纹理坐标等等,例如多个颜色可以使用如COLOR0,COLOR1来描述,也可以都是用COLOR来描述,使用0和1来填充SemanticIndex。
  • Format:描述这个数据的数据类型,如:DXGI_FORMAT_R32G32B32_FLOAT描述3个32位的float数据类型,即12字节长度;而DXGI_FORMAT_R16G16B16A16_UINT表示4个16位的uint数据类型,即8字节长度。
  • InputSlot:输入槽,在前面提到过的,每一个顶点信息都通过一个Vertex Buffer输入让GPU识别,在Direct 3D 11中多个顶点信息可以同时的输入,最多可以有16个,这样就需要让GPU知道当前将使用哪个Vertex Buffer信息,也就是这个值将在0到15之间了。 
  • AlignedByteOffset:内存偏移量,告诉GPU当前缓存内存起始偏移量。
  • InputSlotClass:这个一般使用D3D11_INPUT_PER_VERTEX_DATA来填充,当使用实例数据类型时,将使用D3D11_INPUT_PER_INSTANCE_DATA,这个是比较高级的或许在我们以后的学习当中会遇到,这里我们不大清楚,就先放过。 
  • InstanceDataStepRate:这个用于D3D11_INPUT_PER_INSTANCE_DATA时候,如果不是则必须将其设置为0。

 了解了上面的信息,我们就可以定义我们自己的

D3D11_INPUT_ELEMENT_DESC了,具体代码如下所示:
1       //  Define the input layout
2      D3D11_INPUT_ELEMENT_DESC layout[]  =
3      {
4          {  " POSITION " 0 , DXGI_FORMAT_R32G32B32_FLOAT,  0 0 , D3D11_INPUT_PER_VERTEX_DATA,  0  },
5      };
6      UINT numElements  =  ARRAYSIZE( layout );

 

 Vertex Layout

 顶点布局主要就是为了给顶点着色器(Vertex Shader)提供计算的【注:也许这个描述不正确】,为了创建一个顶点布局就需要顶点着色器输入签名,我们使用ID3DBlob接口对象来描述,而ID3DBlob接口通过D3DX11CompileFromFile来检索顶点着色器包含签名的二进制数据。只要我们有了这个数据,就可以通过ID3D11Device::CreateInputLayout()方法来创建我们的Vertex Layout对象,和使用ID3D11DeviceContext::IASetInputLayout()方法来激活它,具体代码如下:

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

 

 创建Vertex Buffer

 知道了上面的内容我们还需要做一件事,在初始化时我们必须创建一个Vertex Buffer并且承载了这个顶点的数据信息。为了创建Vertex Buffer,必须填充两个结构D3D11_BUFFER_DESC和D3D11_SUBRESOURCE_DATA,然后使用ID3D11Device::CreateBuffer()方法进行创建。D3D11_BUFFER_DESC结构对Vertex Buffer要创建的内容对象进行描述,而

D3D11_SUBRESOURCE_DATA则是包含了具体的数据信息,这些数据信息在创建时会进行拷贝。创建缓存和初始化是在同一时间完成的,在创建后我们可以使用ID3D11DeviceContext::IASetVertexBuffers()方法将其绑定到设备中,那样我们就可以在屏幕上呈现出来了,具体代码如下:
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 (原型拓扑结构)

 从上面我们可以得知,如果要呈现一个三角形那样就需要将三个顶点信息告知GPU,那样如果两个三角形就必须告诉GPU6个顶点的信息,如果一个四边形(两个三角形组成),也就是说有两个顶点是共有的,PrimitiveTopology就是为了解决这个问题的。在四边形中,只要传入是个顶点信息,就可以画出四边形了,如图所示,就可以很好的理解了。

 

 如上图,如果要呈现3a中的图,只要告诉GPU是个顶点,GPU就会直接画出这个四边形了,当然也要注意一下顺序:A B C D,其实在Vertex Buffer中描述的是A B C和B C D,这样 B C两点是共用的,当然在3b图形中也一样。我们只要设置如下代码就可以得到,如下所示:

g_pImmediateContext -> IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

 

接下来就是画出三角形,以上这些都是在设备初始化后进行的。而画出三角形就需要用到顶点和像素着色器,具体代码如下:

void  Render()
{
    
//  Clear the back buffer 
     float  ClearColor[ 4 =  {  0.0f 0.125f 0.3f 1.0f  };  //  red,green,blue,alpha
    g_pImmediateContext -> ClearRenderTargetView( g_pRenderTargetView, ClearColor );

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

    
//  Present the information rendered to the back buffer to the front buffer (the screen)
    g_pSwapChain -> Present(  0 0  );
}

 最终显示结果如下:

 C++Directx11开发笔记三:绘制图形_第2张图片

写到了这里差点浏览器挂掉,OK今天就到这里,发现这个博客园的代码编辑器在Google浏览器里还不是很好用,第一行代码老是要跑到外面出去。 

==================================================
本文可以随意转载,摘抄等非商业用途;
为了尊重作者成果,在转载和摘抄的时候请留下作者名称和出处;
关于作者:网魂小兵
熟悉程序:ASP.NET(C#), C/C++,JAVASCRIPT,SQLSERVER,MYSQL...
熟悉项目:CommunityServer,JQuery,Mangos
研发项目:秘密

你可能感兴趣的:(C++Directx11开发笔记三:绘制图形)