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



在之前的教程中,我们创建了一个最小的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 positionSemanticName。大小写不敏感。

SemanticIndex

Semantic index作为 semantic name的补充.一个顶点可能含有多个本质一样的不同属性,例如,可以含有2texture 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格式的数据长8byte,由416-bit的无符号整形分量构成。

InputSlot

如之前提及过的,Direct3D 11应用通过使用vertex buffer来将顶点数据传递到GPU。在Direct3D 11 中,可以同时向GPU传递多个vertex buffer,准确的说是16个。每一个vertex buffer都被绑定到一个从05 input Slot 索引。InputSlot数据域用来表示GPU将从哪个vertex buffer来获取这个元素。

AlignedByteOffset

一个顶点存放在vertex buffer中,vertex buffer只是一块内存。AlignedByteOffset告诉GPU获取这个元素数据的内存地址。

InputSlotClass

这个数据域通常被设置为D3D11_INPUT_PER_VERTEX_DATA。当应用程序使用instancing特性的时候,可以将一个input layoutInputSlotClass赋值为D3D11_INPUT_PER_INSTANCE_DATA来配合含有instance datavertex 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对象的时候需要vertexshaderinput signature。我们使用D3DX11CompileFromFile返回的ID3DBlob对象来检索表示vertex shaderinput 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_DESCD3D11_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。注意到BCvertex buffer中出现了两次,因为他们被两个三角形共享。


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


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


如果使用triangle stripvertex buffer会相应的缩减为 ABCDE


triangle strip例子中,你可能已经注意到了被BCD定义的第二个三角形,三个顶点,并不遵循顺时针方向。当使用triangle strip,这是很自然的现象。那么,怎么解决它呢?GPU会自动交换从前面的三角形得到的两个点,但是,只是在第2468 ...个三角形的时候才这样做。这样就能够保证被vertex buffer中的点描述的三角形都是正确的方向(顺时针)。除了triangle list triangle stripDirect3D还支持很多类型的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就可以渲染三角形了。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值