三角形元素
为了能让GPU渲染三角形,我们必须告诉它三角形的三个顶点,以2D为例,我们怎样将顶点信息传递给GPU呢?在DX11中, 像这类顶点信息存储在了个BUFF资源中,故称之为顶点BUFF,我们创建的顶点BUFF要够足够装下顶点信息,所以我们要了解顶点布局。
输入布局
每个顶点都有一个位置信息,甚至有其它属性,法线,一个或者多个颜色,贴图坐标等等。顶点布局确定这些属性是如果组织在内存上的,如每个属性的数据类型,每个属性数据大小,以及顺序。如下,我们定义我们的顶点结构只有一个字段类型为XMFLOAT3,这个类型是三浮点向量。
struct SimpleVertex { XMFLOAT3 Pos; // Position };
D3D11_INPUT_ELEMENT_DESC 结构就是用来描述顶点属性,以便GPU理解顶点BUFF的数据格式。接下来我们看一下D3D11_INPUT_ELEMENT_DESC各字段的细节。
SemanticName 语意名字,它是一个字串,描述元素的本质与目的,可以由我们自由定义,不大小敏感
SemanticIndex 语意索引补充了语意名字,一个顶点可能有多个相同性质或目的的属性.例如,它可能有2组贴图坐标,2组颜色, 所以在后面可以再加个索引序号
Format 格式定义了每一个无素数据类型.例如, DXGI_FORMAT_R32G32B32_FLOAT 格式有3个32位浮点数
InputSlot 在DX11,多个顶点BUFF可以同时传给中GPU, 该字段告诉GPU,这个元素应该传到哪个顶点BUFF
AlignedByteOffset 顶点存储在顶点BUFF中,简单的说就是一块内存块 , AlignedByteOffset 字段就是告诉GPU内存位置开取数据元素。
InputSlotClass 这个字段通常有D3D11_INPUT_PER_VERTEX_DATA这个值. 实例化是这D3D高级话题
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);
顶点布局
我们稍接触的顶点Shader与顶点布局有着紧密的联系。因为创建顶点布局时需要顶点Shader的输入签名。我们ID3DBlob对象来取得数据,它表示顶点Shader的签名。有了这数据后,我们可以调用CreateInputLayout来创建顶点布局。然后用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 );
创建顶点布局
在初始化期间,还有一个事情就是创建顶点BUFF来HOLD顶点数据,在DX11中,创建顶点BUFF,我们填充 D3D11_BUFFER_DESC 和 D3D11_SUBRESOURCE_DATA数据结构,然后调用CreateBuffer创建,D3D11_BUFFER_DESC 描述将要创建的BUFF对象,D3D11_SUBRESOURCE_DATA描述实际数据,在创建期间,它将被拷贝到顶点BUFF中。
// 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,所以顶点缓冲中有三个顶点数据,如果我们要渲染二个三角形呢?一种方法就是发送6个顶点数据给GPU,这种布局称为三角链,但这种布局很低效,特别是有连续三角形共享顶点的情况(按惯例,三角形顶点按顺时针), 对于三角形共享顶点,提供了三角带, 第一个三角形的三个顶点存放在顶点BUFF中, 接下来的三角形的三个顶点有上一个三角形的最后两个顶点与新顶点构成。并存储在顶点BUFF中(在三角带中, 三角顶点非按顺时针,但是D3D系统会将2,4,6,8.等自动转换)。
渲染三角形
最后缺的就是一点三角形的渲染,我们创建渲染着色器,及顶点与像素着色器。顶点着色器负责变换每个顶点到正常位置。像素着色器负责计算每个像素最终输出颜色。 为了使用着色器,我们必须先后调用ID3D11DeviceContext::VSSetShader()和ID3D11DeviceContext::PSSetShader() 。最后要做的就是调用ID3D11DeviceContext::Draw(),这就命令就是让GPU使用顶点BUFF,顶点布局,原始拓扑。这个Draw()函数的第一个参数为顶点数,第二个参数为第一顶点的索引。
// Render a triangle
g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 );
g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 );
g_pImmediateContext->Draw( 3, 0 );