Rendering a triangle

Elements of a triangle

一个三角形由它的三个顶点来定义。三个不同位置的顶点能定义一个独一无二的三角形。为了告诉GPU渲染一个三角形,我们必须告诉它这三个点的坐标。下图是一个2D的例子。我们要传递三个坐标分别为(0,0), (0, 1), (1, 0)的点给GPU,然后GPU就有足够的信息来渲染我们想要的三角形。

 

现在我们必须要告诉GPU三个坐标。我们要如何做呢?在D3D10中,顶点信息都存储在一个buffer资源中。一个用来存储顶点信息的buffer就称为顶点缓冲。我们要创建一个足够大的顶点缓冲来存储三个顶点。在D3D10中,应用程序在创建缓冲资源时必须说明顶点缓冲的大小(以字节为单位)。我们知道这个缓冲必须要足够大来装三个顶点,但我们怎么知道每个顶点需要占几个字节空间呢?这就需要理解vertex layout(顶点布局)

 

Input Layout

一个顶点有一个坐标,大部分时候还会有一些其他属性,比如法线,一个或多个颜色值,纹理坐标(用作纹理映射),等等。顶点布局定义了这些属性在内存中如何排列。每种属性使用什么数据类型,每个属性占多大空间,以及在内存中的顺序。顶点通常使用一个结构体来表达,它的大小也就是结构体的大小。

在本例中,我们只需要用到顶点坐标,所以我们用一个单独的D3DXVECTOR3类型定义我们的顶点结构体。这个类型是包含3个浮点元素的向量,通常在3D程序中都用它来作为坐标。

    struct SimpleVertex
    {
        D3DXVECTOR3 Pos;  // Position
    };

现在我们有了一个表示我们的顶点的结构,它可以用来在我们的应用程序中向系统内存中存储顶点信息。尽管如此,当我们向GPU提供顶点缓冲时,我们仅仅向它提供了一块未加工的内存。GPU必须知道顶点布局才能从顶点缓冲中获取到正确的属性。为此要使用输入布局(input layout)。

d3d10中,输入布局是一种用GPU可以理解的方式来描述顶点结构的一种d3d对象。每个顶点属性都可以用D3D10_INPUT_ELEMENT_DESC结构描述。一个应用程序定义一个或多个D3D10_INPUT_ELEMENT_DESC,然后使用这个数组去建立input layout对象,以便描述整个顶点。下面我们看看D3D10_INPUT_ELEMENT_DESC的各个具体属性。

1.       SemanticName:它是一个字符串或一个单词,用来描述这个元素的种类或目的或语义。这个词可以是任意形式。比如,对于一个顶点的坐标,一个好的语义应该是POSITION,语义不区分大小写。

2.       SemanticIndex: 语义索引用来补充语义名。一个顶点可能有多个属性都是相同种类的,比如,可以有两组纹理坐标或2组颜色。我们可以让这两个属性使用相同的语义名“COLOR”,但使用不同的语义索引01,而不使用”COLOR0”和”COLOR1“。

3.       FormatFormat定义了这个元素使用的数据类型。比如DXGI_FORMAT_R32G32B32_FLOAT332位浮点数,这个元素主肖12字节长。

4.       InputSlot: 如前所述,d3d10应用程序使用顶点缓冲向GPU传递顶点数据。在d3d10中,多个顶点缓冲可以同时向GPU输送数据,确切的说是最多同时16个。每个顶点缓冲都绑定到0~15中间的某个输入槽,InputSlot就是告诉GPU,对于这个元素,它应该使用哪个顶点缓冲。

5.       AlignedByteOffset:一个顶点在顶点缓冲中只是简单的一块内存。AlignedByteOffset就告诉GPU在那块内存中从哪里开始获取这个元素。

6.       InputSlotClass:它通常取值为D3D10_INPUT_PER_INSTANCE_DATA。当应用程序使用实例化时,它可以把输入布局的InputSlotClass设置为D3D10_INPUT_PER_INSTANCE_DATA来使用包含着实例数据的顶点缓冲。实例化是d3d中的一个高级话题,在这里不会讨论,对示例程序来说,我们只使用D3D10_INPUT_PER_INSTANCE_DATA

7.       InstanceDataStepRate:它也是为实例化服务的。因为我们不使用实例化,所以通常设置为0

现在我人定义D3D10_INPUT_ELEMENT_DESC数组来创建输入布局。

    // Define the input layout

    D3D10_INPUT_ELEMENT_DESC layout[] =

    {

        { L"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, 

    };

   UINT numElements

 

 

 

Vertex Layout

在下个示例中,我们将解释technique对象和与之相关的着色器(shaders)。现在,我们要专注于为technique创建d3d10顶点布局对象。尽管如此,我们还是会认识到techniqueshaders都是和顶点布局紧密关联的。原因是创建顶点布局对象需要顶点着色器的输入签名(input signature)。我们首先调用techniqueGetPassByIndex()方法来获得一个特效pass对象(表示technique的第一个pass)。然后,我们调用passGetDesc()方法来获取一个pass描述结构。在这个结构内,有一个pIAInputSignature指向一块表示在这个pass中用到的顶点着色器的输入签名的二进制数据。一旦我们获取到这个数据,我们可以调用ID3D10Device::CreateInputLayout()来创建一个顶点布局对象,调用ID3D10Device::IASetInputLayout()来把它设置为激活的顶点布局。代码如下:

    // Create the input layout
    D3D10_PASS_DESC PassDesc;
    g_pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    if( FAILED( g_pd3dDevice->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, &g_pVertexLayout ) ) )
        return FALSE;
    // Set the input layout
    g_pd3dDevice->IASetInputLayout( g_pVertexLayout );

 

Createing Vertex Buffer

在初始化期间还有一件事要做就是创建顶点缓冲。在d3d10中要创建顶点缓冲,需要两个结构体, D3D10_BUFFER_DESC and D3D10_SUBRESOURCE_UP。然后调用ID3D10Device::CreateBuffer()D3D10_BUFFER_DESC描述了顶点缓冲对象,D3D10_USBRESOURCE_UP描述了将要被复制到顶点缓冲中的实际数据。创建和初始化顶点缓冲立即完成,这样我们就不用在后面初始化缓冲了。将要复制到顶点缓冲中的数据是3SimpleVertex类型的顶点。在这个顶点数组中的坐标是为了让我们可以在程序窗口的中央看到三角形。在顶点缓冲创建之后,我们调用ID3D10Device::IASetVertexBuffers()来绑定它到设备上。完整代码如下:

    // Create vertex buffer
    SimpleVertex vertices[] =
    {
        D3DXVECTOR3( 0.0f, 0.5f, 0.5f ),
        D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
        D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
    };
    D3D10_BUFFER_DESC bd;
    bd.Usage = D3D10_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( SimpleVertex ) * 3;
    bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    bd.MiscFlags = 0;
    D3D10_SUBRESOURCE_UP 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_pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

 

 

Primitive Topology 基础拓扑

primitive topology指明了GPU如果获取这三个顶点来渲染三角形。我们讨论它以便渲染单个的三角形。应用程序需要传送三个顶点给GPU。前三个表示第一个三角形,另三个表示第二个三角形。这个拓扑称为三角形链。尽管如此,两个相连的三角形通常共享一些顶点。通常,三角形都按顺时针方向来定义顶点。如果我们把下图中的两个三角ABCCBD发送给GPU,那么顶点缓冲会像这样:ABCCBD

 

 

如果我们在渲染三角形时从前一个三角形中取两个点,而从顶点缓冲中取一个点,这种拓扑就称为三角带(triangle strip)。当渲染一条三角带时,第一个三角形使用顶点缓冲中的前三个顶点来定义,后面的三角形则使用前一个三角形的最后两个点加上顶点缓冲中的下一个点来组成。组成a图的顶点缓冲会这样:ABCD

对于b图,三角链的缓冲如下:ABCCBDCDE

使用三角带的缓冲如下ABCDE。你可能注意到第二个三角形的顶点顺序是BCD,它们没有构成顺时针方向。这是使用三角带时的一个必然现象。为了修正它,GPU会自动交换来自前一个三角形的两个顶点的顺序,并且只交换第246...个三角形。这保证了每个三角形都是按顺时针方向定义的。除了三角链和三角带,d3d10还支持很多其他的基本拓扑,我们会在本例中讨论它们。

在代码中,我们只一个三角形,所以无论我们使用哪种拓扑都没什么关系。

    // Set primitive topology
    g_pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

 

 

Rendering the Triangle

本例中最后要做的就是渲染三角形。如前面提到的那样,本例会使用特效系统来进行渲染。我们用早先获得的technique对象来调用ID3D10EffectTechnique::GetDesc()获取D3D10FX_TECHNIQUE_DESC结构,该结构描述了technique。该结构中的成员之一Passes,指示了该technique包含的pass数量。为了正确的使用这个technique来进行渲染。应用程序应该循环调用每一个pass。在这个循环中,首先调用techniqueGetPassByIndex()函数来获取pass对象,然后调用它的Apply()方法来把特效系统把关联的顶点着色器和渲染状态绑定到图形渲染管线。接下来要做的就是调用ID3D10Device::Draw(),这个函数控制GPU使用当前的顶点缓冲、顶点布局还有基本拓扑类型来进行渲染。Draw()的第一个参数是发送给GPU的顶点数量,第二个参数是要发送的第一个顶点的索引号。由于我们只渲染一个三角形,并且是从顶点缓冲的起点开始渲染,所以我们给这两个参数设置为30.整个渲染过程如下:

    //
    // Render a triangle
    //
    D3D10_TECHNIQUE_DESC techDesc;
    g_pTechnique->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        g_pTechnique->GetPassByIndex( p )->Apply(0);
        g_pd3dDevice->Draw( 3, 0 );
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值