这些天终于受不了诱惑,决定入手Directx11,看了下一个最basic 的demo,总结下自己的理解。
先看看整体框架:所有函数的声明。可以看到和D3d9c差不多,都是初始化窗口,创建设备,清理设备,消息循环和渲染。下面我们将看到发生变化的主要是创建设备的InitDevice()函数,其他都是些MFC框架。
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow );
HRESULT InitDevice();
void CleanupDevice();
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
void Render();
主函数入口,可以看到和Directx9一样,正如前面说的,后面会看到最大的不一样就是在InitDevice()里面初始化不一样的设备而已。
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
// UNREFERENCED_PARAMETER( hPrevInstance );
// UNREFERENCED_PARAMETER( lpCmdLine );
if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;
if( FAILED( InitDevice() ) )
{
CleanupDevice();
return 0;
}
// Main message loop
MSG msg = {0};
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
Render();
}
}
CleanupDevice();
return ( int )msg.wParam;
}
接下来我们集中讨论下InitDevice()。先看下程序一开始定义的全局变量,并讨论InitDevice()中如何初始化下面一些设备接口。
//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = NULL;
ID3D11DeviceContext* g_pImmediateContext = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
ID3D11RenderTargetView* g_pRenderTargetView = NULL;
Directx9里面只有一个设备接口:IDirect3DDevice9,而在Directx11中则有三个:device,immediate contex 和 a swap chain。在d3d9里面,渲染以及创建设备的工作都是有device来负责。但是d3d11里面则分开了,device负责创建resouces,immediate contex负责渲染。而swap chain则是交换链,最典型作用负责交换前后台缓存,front buffer和back buffer。(ps下:swap chain在d3d9里面也有了,不过这个接口在D3d9里面用的很少,因为这个接口都是Direct3D负责管理的,不知道在D3d11里面是不是会用的比较多,有待探索)。 front buffer是现在电脑屏幕上正在展现的内容,back buffer是正在后台被渲染的缓存。当back buffer被绘制好了,则将back buffer和front buffer交换。
为了创建上面说的device,immediate device和swap chain,可以通过调用D3D11CreateDeviceAndSwapChain函数,其原型如下:
HRESULT
D3D11CreateDeviceAndSwapChain( __in IDXGIAdapter *pAdapter,
__in D3D_DRIVER_TYPE DriverType,
__in HMODULE Software,
__in UINT Flags,
__in const D3D_FEATURE_LEVEL *pFeatureLevels,
__in UINT FeatureLevels,
__in UINT SDKVersion,
__in const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
__out IDXGISwapChain **ppSwapChain,
__out ID3D11Device **ppDevice,
__out D3D_FEATURE_LEVEL *pFeatureLevel,
__out ID3D11DeviceContext **ppImmediateContext );
调用实例:
hr = D3D11CreateDeviceAndSwapChain( NULL,
g_driverType,
NULL,
createDeviceFlags,
featureLevels,
numFeatureLevels,
D3D11_SDK_VERSION,
&sd,
&g_pSwapChain,
&g_pd3dDevice,
&g_featureLevel,
&g_pImmediateContext );
再看看d3d9中类似的创建设备函数IDirect3D9::CreateDevice():
HRESULT CreateDevicee( [in] UINT Adapter,
[in] D3DDEVTYPE DeviceType,
[in] HWND hFocusWindow,
[in] DWORD BehaviorFlags,
[in, out] D3DPRESENT_PARAMETERS *pPresentationParameters,
[out, retval] IDirect3DDevice9 **ppReturnedDeviceInterface );
调用实例:
IDirect3D9* g_pD3D;
g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ;
下面我们通过函数CreateRenderTargetView()创建一个render target view(类似于c中用空指针void*创建的一片内存区),并将swap chain的back buffer绑定给它,这样D3d11才能渲染back buffer。我对这个概念也不是很理解,个人感觉像是你想要渲染的东西,必须绑定到一个指定的空显存区,然后d3d11渲染管道的输出会写到这块显存中,即完成渲染。
if( FAILED( g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (LPVOID*)&pBackBuffer ) ) )
return FALSE;
hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release(); if( FAILED( hr ) ) return FALSE;
接下来我们通过调用immediate context的OMSetRenderTargets()将刚创建的render target view绑定到渲染管道,这样渲染管道的输出会自动写到back buffer中。
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );
InitDevice()的最后就是创建一个视口viewport,和d3d9差不多,但是d3d11中视口是必须要创建的。
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)width;
vp.Height = (FLOAT)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pImmediateContext->RSSetViewports( 1, &vp );
最后看看Render().相对d3d9明显简洁了许多,没有了IDirect3DDevice9::begin()和IDirect3DDevice9::end()(连XNA都未能幸免begin和end),直接draw()。
void Render()
{
// Just clear the backbuffer
float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
g_pSwapChain->Present( 0, 0 );
}
剩下几个函数都是和D3d9c差不多了。因为都是一些MFC的框架。