您的位置:首页 > 其它

【DirectX 11 SDK 学习笔记】 Rendering a Triangle

2014-05-13 02:07 633 查看

在之前的教程中,我们创建了一个最小的Direct3D 11应用,仅仅是在窗口中显示纯色。在这个例子中,我们将在上次工作上继续深入,在屏幕上渲染一个三角形。我们将要一步一步建立和三角形相关的数据结构。这个例子的输出就是一个中间会有三角形的窗口。

Elements of a Triangle
一个三角形通过三个点来定义,或者可以称为向量,一组位置不同的三个点定义一个不同的三角形。为了让GPU能够渲染三角形,我们必须告诉GPU将要渲染的三角形三个点的位置。对于2D空间的例子,假设我们要渲染图1中的三角形,我们应当将点(0,
0) (0, 1) (1, 0)传递给GPU,然后GPU通过这些信息,就足以渲染一个三角形了。



那我们如何将这些信息传递给GPU呢?在Direct3D
11 中,顶点信息如位置信息,存放在缓存资源中。不要惊讶,一个存放顶点信息的缓存,通常,被叫做vertex buffer。我们需要创建一个足够放下三个顶点的vertex
buffer,并将顶点位置信息存放在其中。在Direct3D 11中,创建缓存资源的时候,应用程序必指定缓存的字节大小。我们现在知道缓存必须要能够放下三个点,但是每一个点需要多少个字节呢?为了弄明白

这个问题,我们需要进一步了解vertexlayout。

Input Layout

一个顶点通常有位置信息,或者压根就没有位置信息,当然,还有别的属性,例如法线,一个或多个颜色,纹理坐标(用来纹理映射),等等。Vertex layout定义这些属性在内存中如何存放:每个属性使用什么样的数据类型,每个属性占多大的空间,还有这些属性在内存中的顺序,应为不同的属性通常都会具有不同的类型,有点类似于
C语言结构体中的不同成员,一个vertex通常通过一个结构体来描述。vertex的大小可以方便的通过结构体的大小来获得。

在这个教程中,我们只用到了顶点的位置属性,因此,我们将我们的顶点结构体定义为只有一个XMFLOAT3类型的数据域,XMFLOAT3是一个有三个浮点分量的数组,在3D中经常被用来表示位置。

structSimpleVertex

{

XMFLOAT3 Pos; // Position

};

现在,我们有一个能够描述位置信息的结构体,要留意顶点信息在系统内存中的存放。然而,当我们把包含顶点信息vertex buffer喂给GPU的时候,我们只是传递了一块内存。GPU为了从缓存中提取出正确的属性信息,也需要知道vertex
layout,为了满足需求,我们需要使用 input layout。

在Direct3D 11中,input
layout是一个使用GPU能够识别的方式来描述顶点结构的Direct3D对象,D3D11_INPUT_ELEMENT_DESC结构体能够描述每一个顶点属性,每个应用会定义一个包含一个或者多个D3D11_INPUT_ELEMENT_DESC结构体的数组,那么我们用这个结构体数组来创建input
layout 对象,就能够表示所有的顶点组合了。现在,我们来仔细看一看这个结构体的每一个成员域:



SemanticName
SemanticName是一个含有一个标识符的字符串,用来描述这个元素的本质或者目的.这个标识符和C语言的标识符一致,可以任意选择.例如,POSITION就是一个非常适合vertex's
position的SemanticName。大小写不敏感。
SemanticIndex
Semantic index作为
semantic name的补充.一个顶点可能含有多个本质一样的不同属性,例如,可以含有2组texture
coordinates,或者是2组颜色,我们可以在后面加上数字来区别,像 "COLOR0" and
"COLOR1",这两个元素拥有同一个semantic name "COLOR",但是有不同的SemanticIndex
0 和1.
Format
Format定义了这个元素将以什么数据类型来使用。例如:一个DXGI_FORMAT_R32G32B32_FLOAT格式的数据有3个
32-bit 的浮点数分量,总共有12 byte长。一个DXGI_FORMAT_R16G16B16A16_UINT格式的数据长8个byte,由4个16-bit的无符号整形分量构成。
InputSlot
如之前提及过的,Direct3D 11应用通过使用vertex
buffer来将顶点数据传递到GPU。在Direct3D 11
中,可以同时向GPU传递多个vertex buffer,准确的说是16个。每一个vertex
buffer都被绑定到一个从0到5的
input Slot 索引。InputSlot数据域用来表示GPU将从哪个vertex
buffer来获取这个元素。
AlignedByteOffset
一个顶点存放在vertex buffer中,vertex
buffer只是一块内存。AlignedByteOffset告诉GPU获取这个元素数据的内存地址。
InputSlotClass
这个数据域通常被设置为D3D11_INPUT_PER_VERTEX_DATA。当应用程序使用instancing特性的时候,可以将一个input
layout的InputSlotClass赋值为D3D11_INPUT_PER_INSTANCE_DATA来配合含有instance
data的vertex buffer工作。Instancing是
Direct3D的高级话题,不会在这里细致讨论。我们目前只是使用D3D11_INPUT_PER_VERTEX_DATA值。
InstanceDataStepRate
这个域在使用instancing特性的时候才需要。所以只需要设置为0.
现在,我们可以定义D3D11_INPUT_ELEMENT_DESC数组来创建inputlayout:

// Define the inputlayout

D3D11_INPUT_ELEMENT_DESC layout[] =

{

{ "POSITION", 0,DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },

};

UINT numElements = ARRAYSIZE(layout);

Vertex Layout

在下一个教程中,进一步探索这个技术并且配合 shader一起工作.现在我们只是集中精力在创建Direct3D
11 vertex layout 对象这个技术点上。但是我们将要学习的 vertex shader和
vertex layout紧密耦合,这就是为什么创建vertex layout对象的时候需要vertexshader的input
signature。我们使用D3DX11CompileFromFile返回的ID3DBlob对象来检索表示vertex
shader的input signature
的二进制数据。当我们获得这个数据,我们能够调用ID3D11Device::CreateInputLayout() 来创建
vertex layout对象,然后调用ID3D11DeviceContext::IASetInputLayout()来将其设置为active
vertex layout。下面的代码可以完成上述工作:

// Create the inputlayout

if( FAILED( g_pd3dDevice->CreateInputLayout( layout, numElements,pVSBlob->GetBufferPointer(),

pVSBlob->GetBufferSize(),&g_pVertexLayout ) ) )

return FALSE;

// Set the input layout

g_pImmediateContext->IASetInputLayout( g_pVertexLayout );

Creating Vertex Buffer

在初始化的过程中,我们还需要做的一件事情是创建 vertex buffer并保存顶点数据。在DIrect3D
11中,创建 vertex buffer,我们需要填充两个结构体:D3D11_BUFFER_DESC和D3D11_SUBRESOURCE_DATA,然后调用ID3D11Device::CreateBuffer().
D3D11_BUFFER_DESC描述如何创建vertex buffer对象,D3D11_SUBRESOURCE_DATA描述在创建的时候需要拷贝到
vertex buffer中的真实数据。Vertexbuffer的创建和初始化的工作一旦完成,我们以后就不需要初始化这个缓存了。将要拷贝到
vertex buffer的数据是顶点,一个由三个简单顶点结构体构成的数组。顶点数组中是我们之前选定好的数据,当我们用 shader渲染之后,就能在程序窗口中看到一个三角形。在vertex
buffer创建之后,我们就可以调用ID3D11DeviceContext::IASetVertexBuffers()来绑定到device。下面是完整代码:



// Create vertexbuffer

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_DESCbd;

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_DATAInitData;

ZeroMemory( &InitData, sizeof(InitData) );

InitData.pSysMem =vertices;

if( FAILED(g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer ) ))

return FALSE;



// Set vertexbuffer

UINT stride = sizeof( SimpleVertex );

UINT offset = 0;

g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer,&stride, &offset );

Primitive Topology

Primitive topology涉及到GPU如何获得渲染一个三角形的三个顶点。我们的讨论基于渲染单个三角形,应用程序需要传递三个顶点数据到GPU,因此,vertexbuffer中有三个顶点数据。如果我们要渲染2个三角形呢?一种方法是我们传递给GPU6个顶点,头三个点定义第一个三角形,接着的三个点定义另一个三角形,这样的拓扑叫triangle
list ,优点在于容易理解,但是缺点在于效率不高,特别是在渲染共用顶点的多个三角形的时候。



例如,左边的图3a中有一个由(为了方便,三角形一般按顶点的顺时针方向定义)构成的正方形,如果我们功过triangle
list 方式将这两个三角形传递给GPU,我们的vertex
buffer 看起来应该是这样:A B C C B D。注意到BC在vertex
buffer中出现了两次,因为他们被两个三角形共享。

在渲染第二个三角形的时候,如果我们能够告诉GPU用之前三角形的2个点,从vertex
buffer 获得一个新的点,而不是从vertex buffer中直接获取三个点,我们就可以让vertex
buffer尽可能的小。事实证明,这已经被Direct3D支持了,这个技术叫triangle strip。在渲染trianglestrip的时候,每一个开始的三角形被vertex
buffer的前三个点定义,紧接着的三角形用之前三角形的两个点和vertex buffer接下来的一个点定义。如果用图3a做例子,使用triangle
strip,vertex buffer中的数据看起来是这样的:A B C D

头三个点 ABC定义第第一个三角形triangle
strip拓扑结构,vertex buffer的大小由原来的6个顶点缩小成了4个顶点。类似的,对于图3b中三个三角形,如果使用triangle
list ,则需要一个这样的vertex buffer: ABC CBD CDE

如果使用triangle strip,vertex
buffer会相应的缩减为 ABCDE

在triangle strip例子中,你可能已经注意到了被BCD定义的第二个三角形,三个顶点,并不遵循顺时针方向。当使用triangle
strip,这是很自然的现象。那么,怎么解决它呢?GPU会自动交换从前面的三角形得到的两个点,但是,只是在第2,4,6,8
...个三角形的时候才这样做。这样就能够保证被vertex buffer中的点描述的三角形都是正确的方向(顺时针)。除了triangle
list 和triangle strip,Direct3D还支持很多类型的primitive
topology,我们在这里暂不讨论。

在我们的代码中,我们只有一个三角形,如何指定,无关紧要,但是,至少要指定点什么,所以,就triangle list了。

// Set primitivetopology

g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

编码调试:

1.加入头文件和lib目录

2.加入lib库

3.设置为unicode编码

问题1:编译fx报错



查了下原因,是因为fx文件会默认用HLSL编译器编译,是另外一种新的方式,但是我们这里只是写了一个类似于脚本的文件,然后自己调用编译函数来编译,所以取消默认即可。



输出结果中只有一个蓝屏,没有三角形:



认真排错,发现是layout写错了



//definethe input layout

D3D11_INPUT_ELEMENT_DESClayout[] = {

{"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0,D3D11_INPUT_PER_VERTEX_DATA, 0 },

};

而且

struct SimpleVertex

{

XMFLOAT3Pos;

};



//createvertex buffer

SimpleVertexvertices[] = {

XMFLOAT3(0.0f,0.5f, 0.5f),

XMFLOAT3(0.5f,-0.5f, 0.5f),

XMFLOAT3(-0.5f,-0.5f, 0.5f),

};



所以,将D3D11_INPUT_ELEMENT_DESC结构体的format域改为DXGI_FORMAT_R32G32B32_FLOAT就可以渲染三角形了。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐