最好的學習方式就是動手操作。以下代碼可以在書籍官方網站上下載。
以下基於Windows7+VS2010 開發環境。並確保您已經安裝DXSDK。
開始編寫第一個DirectX程序。
1、運行VS2010,新建項目 BlankWindow
2、添加Windows創建代碼。此後,當前項目會作爲後續練習項目的模版
1、添加main.cpp源文件
2、編寫win32 sdk代碼,程序入口
#include <Windows.h> int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE prevInstance, LPWSTR cmdLine,int cmdShow) { return 0; }
這裏我們使用wWinMain代替WinMain,支持Unicode參數。對應參數3類型LPWSTR
3、初始化窗口、消息處理
#include <Windows.h> LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE prevInstance, LPWSTR cmdLine,int cmdShow) { UNREFERENCED_PARAMETER(prevInstance); UNREFERENCED_PARAMETER(cmdLine); // 註冊窗口 WNDCLASSEX wndClass={0}; wndClass.cbSize = sizeof(WNDCLASSEX); wndClass.style = CS_HREDRAW|CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL,IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wndClass.lpszMenuName=NULL; wndClass.lpszClassName = "DX11GeoWindowClass"; if(!RegisterClassEx(&wndClass)){ return -1; } // 創建窗口 RECT rc = {0,0,640,480}; AdjustWindowRect(&rc,WS_OVERLAPPEDWINDOW,FALSE); HWND hwnd = CreateWindowA("DX11GeoWindowClass","Blank Win32 Window", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,rc.right-rc.left,rc.bottom-rc.top,NULL,NULL,hInstance,NULL); if(!hwnd) return -1; // 顯示 ShowWindow(hwnd,cmdShow); // 消息處理 MSG msg = {0}; while(msg.message!=WM_QUIT){ if(PeekMessage(&msg,0,0,0,PM_REMOVE)){ TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; } LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { switch(message){ case WM_PAINT: break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,message,wParam,lParam); } return 0; }
Win32 SDK窗口程序基本框架,可以參考MSDN。這裏,UNREFERENCED_PARAMETER用於告訴編譯器不使用的參數,避免編譯器警告提示。
養成處理所有警告提示的良好的編程風格
運行效果,
3、新項目 BlankD3DWindow
新建項目到現有解決方案
進入項目屬性,配置附加鏈接庫。
d3d11.lib;d3dx11.lib;dxerr.lib 注意,半角逗號分隔
設置鏈接庫引用路徑
$(DXSDK_DIR)Include; $(DXSDK_DIR)Lib\x86;
初始化Direct3D
步驟,
- 定義創建設備的驅動類型、特性水平、交換鏈
- 創建設備類型
- 創建渲染目標
- 設置視圖
創建設備的驅動類型包含,
- 硬件加速:性能最好,也稱爲HAL(硬件抽象層)
- WARP :DX11新加入。Windows高級光柵化平臺,採用軟件模擬方式。微軟對指令進行了高度優化
- 軟件驅動 : 開發者編寫自己的渲染驅動插件。不需要DXSDK支持。但不建議在有高性能要求的程序中使用
- REFERENCE:軟件模擬所有D3D特性,速度很慢。一般用於開發,需要DXSDK支持
- NULL:本質上同REFERENCE,但沒有渲染功能
特性水平包含,
- 11.0
- 10.1
- 10.0
什麼是交換鏈?
用於交換前後緩衝(可以理解爲前一幀、後一幀,使得連續輸出),包含如下描述屬性
- 緩衝數量(可以多組)
- 輸出長寬
- 緩衝格式
- FPS。一般液晶顯示器60HZ
定義交換鏈描述。
DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) ); swapChainDesc.BufferCount = 1;//緩衝數量 swapChainDesc.BufferDesc.Width = width;//輸出長寬 swapChainDesc.BufferDesc.Height = height; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;//格式 swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;//FPS swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = hwnd; swapChainDesc.Windowed = true;//是否窗口形式 false; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0;
創建交換鏈,設備上下文,
unsigned int creationFlags = 0; #ifdef _DEBUG creationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif HRESULT result; unsigned int driver = 0; //** 創建device、上下文、交換鏈 for( driver = 0; driver < totalDriverTypes; ++driver ) { result = D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver], 0, creationFlags, featureLevels, totalFeatureLevels, D3D11_SDK_VERSION, &swapChainDesc, &swapChain_, &d3dDevice_, &featureLevel_, &d3dContext_ ); if( SUCCEEDED( result ) ) { driverType_ = driverTypes[driver]; break; } } if( FAILED( result ) ) { DXTRACE_MSG( "Failed to create the Direct3D device!" ); return false; }
創建渲染目標
ID3D11Texture2D* backBufferTexture; result = swapChain_->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBufferTexture ); if( FAILED( result ) ) { DXTRACE_MSG( "Failed to get the swap chain back buffer!" ); return false; } //** 創建渲染目標 result = d3dDevice_->CreateRenderTargetView( backBufferTexture, 0, &backBufferTarget_ ); if( backBufferTexture ) backBufferTexture->Release( ); if( FAILED( result ) ) { DXTRACE_MSG( "Failed to create the render target view!" ); return false; } d3dContext_->OMSetRenderTargets( 1, &backBufferTarget_, 0 );
設定視角
D3D11_VIEWPORT viewport; viewport.Width = static_cast<float>(width); viewport.Height = static_cast<float>(height); viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; viewport.TopLeftX = 0.0f; viewport.TopLeftY = 0.0f; d3dContext_->RSSetViewports( 1, &viewport );
執行繪圖和呈現
if( d3dContext_ == 0 ) return; float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f }; d3dContext_->ClearRenderTargetView( backBufferTarget_, clearColor ); swapChain_->Present( 0, 0 );
開發電腦硬件,
T430筆記本自帶的NVS5400M顯卡支持DX11,128位存儲器接口,分2G、1G顯存。T410用的是NVS3100M顯卡,不支持DX11。
調試DX11特性,需要支持DX11的顯卡。
建立模版項目
D3D初始化和銷燬代碼在很多項目中需要用到,爲了避免重複編寫,這裏生成一個類DX11DemoBase。
class Dx11DemoBase { public: Dx11DemoBase(); virtual ~Dx11DemoBase(); bool Initialize( HINSTANCE hInstance, HWND hwnd ); void Shutdown( ); virtual bool LoadContent( ); virtual void UnloadContent( ); virtual void Update( float dt ) = 0; virtual void Render( ) = 0; protected: HINSTANCE hInstance_; HWND hwnd_; D3D_DRIVER_TYPE driverType_; D3D_FEATURE_LEVEL featureLevel_; ID3D11Device* d3dDevice_; ID3D11DeviceContext* d3dContext_; IDXGISwapChain* swapChain_; ID3D11RenderTargetView* backBufferTarget_; };
用法,
具體項目實現中,繼承這個類,
#include "dx11demobase.h" class BlankDemo : public Dx11DemoBase { public: BlankDemo(void); virtual ~BlankDemo(void); bool LoadContent( ); void UnloadContent( ); void Update( float dt ); void Render( ); };
main.cpp 添加調用
BlankDemo demo; // 初始化DX對象 bool result = demo.Initialize( hInstance, hwnd ); if(result==false) return -1; // 消息處理 MSG msg = {0}; while(msg.message!=WM_QUIT){ if(PeekMessage(&msg,0,0,0,PM_REMOVE)){ TranslateMessage(&msg); DispatchMessage(&msg); } //** DX對象動作 demo.Update(0.0f); demo.Render(); } // 釋放DX對象 demo.Shutdown();
另一種寫法採用std::auto_prt
std::auto_ptr<Dx11DemoBase> demo( new BlankDemo( ) ); // 初始化DX對象 bool result = demo->Initialize( hInstance, hwnd ); if(result==false) return -1; // 消息處理 MSG msg = {0}; while(msg.message!=WM_QUIT){ if(PeekMessage(&msg,0,0,0,PM_REMOVE)){ TranslateMessage(&msg); DispatchMessage(&msg); } //** DX對象動作 demo->Update(0.0f); demo->Render(); } // 釋放DX對象 demo->Shutdown();
std::auto_prt的好處是可以在超出作用域後自動釋放內存。即使異常退出,也會自動釋放。推薦做法。
最終運行截圖,
DX編程錯誤處理,
支持三個方法,
- TCHAR* DXGetErrorDescription( HRESULT hr ) //得到詳細的錯誤描述
- TCHAR* DXGetErrorString( HRESULT hr ) //只返回錯誤代碼,沒有描述
- HRESULT DXTrace( CHAR * strFile, DWORD dwline, HRESULT hr, CHAR *strMsg, BOOL bPopMsgBox )
DXTrace 顯示帶詳細錯誤描述的文本消息,並且帶代碼文件名和出錯行提示。根據不同參數,對應三個宏,
- DXTRACE_ERR( str, hr )//在調試窗口顯示自定義文本和hr對應的詳細錯誤描述
- DXTRACE_ERR_MSGBOX( str, hr ) //彈出消息框
- DXTRACE_MSG( str ) //在調試輸出窗口顯示自定義文本
這些宏在調試DX程序中,很有用。
後面將學習2D渲染實現。