Direct3D 11 Tutorial 2: Rendering a Triangle_Direct3D 11 教程2:渲染一个三角形

概要

在以前的教程中,咱们创建了一个最小的Direct3D 11的应用程序,它用来在窗口上输出一个单一颜色。在本次教程中,咱们将扩展这个应用程序,在屏幕上渲染出一个单一颜色的三角形。咱们将经过设置数据机构的过程关联到三角形。git

这个教程的输出结果是在窗口中央渲染出一个三角形。github

 

资源目录

(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial02数组

Github-LearnDirectX-DX3D11 tutorial02 (源码已上传至Github)ide

 

三角形的元素

三角形由其三个点定义,也称为顶点。 具备惟一位置的一组三个顶点定义了惟一的三角形。 为了让GPU渲染三角形,咱们必须告诉它三角形的三个顶点的位置。举一个2D的例子,假设咱们但愿渲染一个三角形,例如图1中的三角形。咱们将三个顶点与位置(0,0)(0,1)和(1,0)一块儿传递给GPU,而后 GPU有足够的信息来渲染咱们想要的三角形。布局

图1.由三个顶点定义的2D三角形spa

因此如今咱们知道咱们必须将三个位置传递给GPU才能渲染三角形。 咱们如何将这些信息传递给GPU? 在Direct3D 11中,诸如位置的顶点信息存储在缓冲区资源中。 用于存储顶点信息的缓冲区被称为顶点缓冲区,这并不奇怪。 咱们必须为三个顶点建立一个足够大的顶点缓冲区,并用顶点位置填充它。 在Direct3D 11中,应用程序必须在建立缓冲区资源时指定缓冲区大小(以字节为单位)。 咱们知道缓冲区必须足够大才能容纳三个顶点,但每一个顶点须要多少字节? 要回答这个问题,须要了解顶点布局。3d

 

输入布局

顶点有一个位置。 一般,它还具备其余属性,例如法线,一种或多种颜色,纹理坐标(用于纹理映射)等。 顶点布局定义了这些属性在内存中的位置:每一个属性使用的数据类型,每一个属性的大小以及内存中属性的顺序。 由于属性一般具备不一样的类型,相似于C结构中的字段,因此顶点一般由结构表示。 顶点的大小能够方便地从结构的大小中得到。orm

在本教程中,咱们只处理顶点的位置。 所以,咱们使用XMFLOAT3类型的单个字段定义顶点结构。 此类型是三个浮点组件的向量,一般是用于3D位置的数据类型。对象

struct SimpleVertex
{
    XMFLOAT3 Pos;  // Position
};

  

咱们如今有一个表示咱们的顶点的结构。 它负责在咱们的应用程序中将顶点信息存储在系统内存中。 然而,当咱们向GPU提供包含顶点的顶点缓冲区时,咱们只是给它一块内存。 GPU还必须知道顶点布局,以便从缓冲区中提取正确的属性。 要实现此目的,须要使用输入布局。blog

在Direct3D 11中,输入布局是Direct3D对象,它以GPU能够理解的方式描述顶点的结构。 可使用D3D11_INPUT_ELEMENT_DESC结构描述每一个顶点属性。 应用程序定义一个或多个D3D11_INPUT_ELEMENT_DESC的数组,而后使用该数组建立输入布局对象,该对象将顶点描述为一个总体。 如今咱们将详细介绍D3D11_INPUT_ELEMENT_DESC的字段。

SemanticName

SemanticName是一个字符串,其中包含描述此元素的性质或目的(或语义)的单词。 这个词能够是C标识符能够的任何形式,也能够是咱们选择的任何形式。 例如,顶点位置的良好语义名称是POSITION。 语义名称不区分大小写。

SemanticIndex

SemanticIndex补充了语义名称。 顶点能够具备相同性质的多个属性。 例如,它能够具备2组纹理坐标或2组颜色。 不是使用附加了数字的语义名称,例如“COLOR0”和“COLOR1”,这两个元素能够共享单个语义名称“COLOR”,具备不一样的语义索引01

Format

格式定义要用于此元素的数据类型。 例如,DXGI_FORMAT_R32G32B32_FLOAT的格式有三个32位浮点数,使元素长12个字节。 DXGI_FORMAT_R16G16B16A16_UINT的格式有四个16位无符号整数,使元素长8个字节。

InputSlot

如前所述,Direct3D 11应用程序经过使用顶点缓冲区将顶点数据传递给GPU。 在Direct3D 11中,能够同时向GPU提供多个顶点缓冲区,准确地说是16。 每一个顶点缓冲区都绑定到015之间的输入槽号.InputSlot字段告诉GPU它应该为该元素获取哪一个顶点缓冲区。

AlignedByteOffset

顶点存储在顶点缓冲区中,顶层缓冲区只是一块内存。 AlignedByteOffset字段告诉GPU开始获取此元素数据的内存位置。

InputSlotClass

该字段的值一般为D3D11_INPUT_PER_VERTEX_DATA。 当应用程序使用实例化时,它能够将输入布局的InputSlotClass设置为D3D11_INPUT_PER_INSTANCE_DATA以使用包含实例数据的顶点缓冲区。 Instancing是一个高级的Direct3D主题,这里再也不讨论。 对于咱们的教程,咱们将专门使用D3D11_INPUT_PER_VERTEX_DATA

InstanceDataStepRate

该字段用于实例化。 因为咱们不使用实例化,所以不使用此字段,必须将其设置为0

如今咱们能够定义D3D11_INPUT_ELEMENT_DESC数组并建立输入布局:

// 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描述了在建立过程当中将复制到顶点缓冲区的实际数据。 顶点缓冲区的建立和初始化是一次完成的,所以咱们之后不须要初始化缓冲区。 将复制到顶点缓冲区的数据是顶点,即三个简单结构的数组。 选择顶点数组中的坐标,以便在使用着色器渲染时在应用程序窗口的中间看到一个三角形。 建立顶点缓冲区后,咱们能够调用ID3D11DeviceContext :: IASetVertexBuffers()将其绑定到设备。 完整的代码以下所示:

// 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 );

  

原始拓扑

原始拓扑是指GPU如何得到渲染三角形所需的三个顶点。 咱们在上面讨论过,为了渲染单个三角形,应用程序须要向GPU发送三个顶点。 所以,顶点缓冲区中有三个顶点。 若是咱们想渲染两个三角形怎么办? 一种方法是将6个顶点发送到GPU。 前三个顶点定义第一个三角形,后三个顶点定义第二个三角形。 此拓扑称为三角形列表。 三角形列表具备易于理解的优势,但在某些状况下它们效率很是低。 当连续渲染的三角形共享顶点时会出现这种状况。 例如,图3a左侧显示了由两个三角形组成的正方形:ABC和CB D.(按照惯例,三角形一般经过按顺时针顺序列出它们的顶点来定义。)若是咱们使用三角形列表将这两个三角形发送到GPU ,咱们的顶点缓冲区会这样:

A B C C B D

请注意,B和C在顶点缓冲区中出现两次,由于它们由两个三角形共享。

图3a包含一个由两个三角形组成的正方形; 图3b包含由三个三角形组成的五边形。

若是咱们能够告诉GPU在渲染第二个三角形时,咱们可使顶点缓冲区更小,而不是从顶点缓冲区获取全部三个顶点,使用前一个三角形中的2个顶点,并从顶点缓冲区中仅获取1个顶点。 事实证实,这是由Direct3D支持的,拓扑结构称为三角形条带。 渲染三角形条带时,第一个三角形由顶点缓冲区中的前三个顶点定义。 下一个三角形由前一个三角形的最后两个顶点加上顶点缓冲区中的下一个顶点定义。 以图3a中的方块为例,使用三角形条带,顶点缓冲区看起来像:

A B C D

前三个顶点A B C定义第一个三角形。 第二个三角形由B和C定义,即第一个三角形的最后两个顶点加上D.所以,经过使用三角形条带拓扑,顶点缓冲区大小从6个顶点变为4个顶点。 相似地,对于三个三角形,例如图3b中的三角形,使用三角形列表将须要顶点缓冲区,例如:

A B C C B D C D E

使用三角形条带,顶点缓冲区的大小显着减小:

A B C D E
你可能已经注意到,在三角形条带示例中,第二个三角形定义为B C D.这三个顶点不造成顺时针顺序。 这是使用三角形条带的天然现象。 为了克服这个问题,GPU会自动交换来自前一个三角形的两个顶点的顺序。 它只对第二个三角形,第四个三角形,第六个三角形,第八个三角形等执行此操做。 这确保每一个三角形由顶点以正确的缠绕顺序(在这种状况下为顺时针方向)定义。 除了三角形列表和三角形条带外,Direct3D 11还支持许多其余类型的原始拓扑。 咱们不会在本教程中讨论它们。

在咱们的代码中,咱们有一个三角形,因此咱们指定的并不重要。 可是,咱们必须指定一些内容,所以咱们选择了三角形列表。

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

  

渲染三角形

缺乏的最后一项是执行三角形实际渲染的代码。咱们建立了两个用于渲染的着色器,顶点着色器和像素着色器。顶点着色器负责将三角形的各个顶点转换为正确的位置。像素着色器负责计算三角形的每一个像素的最终输出颜色。这将在下一个教程中详细介绍。要使用这些着色器,咱们必须分别调用ID3D11DeviceContext :: VSSetShader()和ID3D11DeviceContext :: PSSetShader()。咱们作的最后一件事是调用ID3D11DeviceContext :: Draw(),它命令GPU使用当前顶点缓冲区,顶点布局和原始拓扑进行渲染。 Draw()的第一个参数是要发送到GPU的顶点数,第二个参数是要开始发送的第一个顶点的索引。由于咱们渲染一个三角形而且咱们从顶点缓冲区的开头渲染,因此咱们分别使用3和0做为两个参数。整个三角形渲染代码以下所示:

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

  

最终渲染效果:

相关文章
相关标签/搜索