上一篇所介绍的流程是每个DX程序初始化的必需代码,运行代码可以得到一个空窗口。下面我们就开始在这个窗口上画点东西。正如学编程语言都是从hello world开始,学图形学一般都是从画三角形开始,之前学OPENGL是这样。而MS的官方教程中,Tutorial02就渲染了一个三角形。
基本流程
新增变量
ID3D11VertexShader* g_pVertexShader = NULL;
ID3D11PixelShader* g_pPixelShader = NULL;
ID3D11InputLayout* g_pVertexLayout = NULL;
ID3D11Buffer* g_pVertexBuffer = NULL;
从DX10开始,固定管线被完全移除,取而代之的是各种着色器。最基本的两种着色器,顶点着色器和像素着色器,是渲染一个图形所必备的。
顶点着色器,顾名思义,是对于用户定义的顶点进行变换操作,是渲染管线的入口;像素着色器则是对顶点所划出的区域,进行逐像素操作。
如果程序中只包含这两种着色器,那么像素着色器的输入,将直接来自于顶点着色器的输出。
顶点:vertex,不同于点point。它包含了更多信息,其中最基本的,也是必需的信息,就是顶点位置。所以我们定义一个顶点结构体,先把最基本的位置信息作为其内部成员。
struct Vertex
{ XMFLOAT3 pos;};
XM-XNA Math库,DX11中应用的数学库。在dx之前的版本中,用的是D3DXVECTOR3,和XMFLOAT3所代表的结构相同。
1、创建并设置顶点缓存
输入顶点着色器的,需要是一块顶点缓存。dx11中若要创建顶点缓存,需要告知缓存的描述信息(D3D11_BUFFER_DESC),缓存的填充数据(D3D11_SUBRESOURCE_DATA)。
缓存描述信息中描述了该缓存的用途(BIND VERTEX),该缓存的大小(sizeof(Vertex)*顶点数量);
缓存的填充数据,就是将我们定义的顶点数组赋给其pSysMem成员完成数据绑定。
最后调用device的CreateBuffer方法,将以上二者和我们想得到的g_pVertexBuffer 地址传入。
创建成功后,context调用IASetVertexBuffers方法来设置顶点缓存。
此外,还需要设置顶点的拓扑结构,context调用IASetPrimitiveTopology方法。
所谓拓扑结构就是几何物体与其自身相连接的固有属性。
代码示例:
//InitDevice函数
//新增代码
//缓存信息描述
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( Vertex ) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
//缓存填充数据
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = vertices;
//缓存创建
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
// 设置顶点缓存
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
// 设置顶点的拓扑组织结构(三角形列表、三角形扇、三角形条带…)
g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
2、创建着色器,完成输入装配
一般着色器的创建流程就两步:1、编译着色器代码片;2、根据编译得到的缓存数据创建着色器并保存。
而顶点着色器作为渲染管线的入口,还承担着接收原始输入信息的任务。所以在创建完着色器之后,我们还需要完成输入装配(Input layout)。也就是告诉顶点着色器我们输入数据的组织形式,这样着色器才知道该怎样调用输入的一堆数据。
编译步骤,需要指明着色器文件路径,着色器的入口函数名称,着色器解释模型。
代码示例:
//CompileShaderFromFile函数
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
HRESULT hr = S_OK;
//D3DCOMPILE_ENABLE_STRICTNESS:禁止过时的语法
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
dwShaderFlags |= D3DCOMPILE_DEBUG;//编译着色器时加入调试信息
#endif
ID3DBlob* pErrorBlob;
hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel,
dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
if( FAILED(hr) )
{
if( pErrorBlob != NULL )
OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
if( pErrorBlob ) pErrorBlob->Release();
return hr;
}
if( pErrorBlob ) pErrorBlob->Release();
return S_OK;
}
// InitDevice函数
// 新增代码
ID3DBlob* pVSBlob = NULL;
// 编译vertex shader代码文件
hr = CompileShaderFromFile( L"Tutorial02.fx", "VS", "vs_4_0", &pVSBlob );
if( FAILED( hr ) )
{
// 处理错误...
return hr;
}
// 创建 vertex shader
hr = g_pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader );
if( FAILED( hr ) )
{
pVSBlob->Release();
return hr;
}
// 编译 pixel shader代码文件
ID3DBlob* pPSBlob = NULL;
hr = CompileShaderFromFile( L"Tutorial02.fx", "PS", "ps_4_0", &pPSBlob );
if( FAILED( hr ) )
{
// 处理错误...
return hr;
}
// 创建 pixel shader
hr = g_pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader );
pPSBlob->Release();
if( FAILED( hr ) )
return hr;
// 定义input layout
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
// 此处numElements=1
UINT numElements = ARRAYSIZE( layout );
// 创建 input layout
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, pVSBl