來學習一下DX11
首先是環境的搭建,我的系統是Win10,ide是VS2013,
由與DirectX的SDK已經集成到了VS中,所以不需要再去下載DirectX11的SDK了。
如果是在win7 + VS2010的還是需要按網上的那一套安裝一遍。
這里會遇到一個問題:
一些代碼中會使用到d3dx11.lib或者之前的lib
在我的環境中是無法使用的,具體可以看
https://msdn.microsoft.com/en-us/library/windows/desktop/ee663275
中間有介紹:
D3DX is not considered the canonical API for using Direct3D in Windows 8 and later and therefore isn't included with the corresponding Windows SDK. Investigate alternate solutions for working with the Direct3D API. For legacy projects, such as the Windows 7 (and earlier) DirectX SDK samples, the following steps are necessary to build applications with D3DX using the DirectX SDK:
-
Modify the project’s VC++ directories as follows to use the right order for SDK headers and libraries.
- i. Open Properties for the project and select the VC++ Directories page.
- ii. Select All Configurations and All Platforms.
-
iii. Set these directories as follows:
- Executable Directories: <inherit from parent or project defaults> (On right-side drop-down)
- Include Directories: $(IncludePath);$(DXSDK_DIR)Include
- Include Library Directories: $(LibraryPath);$(DXSDK_DIR)Lib\x86
- iv. Click Apply.
- v. Choose the x64 Platform.
-
vi. Set the
Library Directory as follows:
- Library Directories: $(LibraryPath);$(DXSDK_DIR)Lib\x64
- Wherever "d3dx9.h", "d3dx10.h", or "d3dx11.h" are included in your project, be sure to explicitly include "d3d9.h", "d3d10.h" and "dxgi.h", or "d3d11.h" and "dxgi.h" first to ensure you are picking up the newer version. You can disable warning C4005 if needed; however, this warning indicates you are using the older version of these headers.
- Remove all references to DXGIType.h in your project. This header doesn't exist in the Windows SDK, and the DirectX SDK version conflicts with the new winerror.h.
- All D3DX DLLs are installed onto your development computer by the DirectX SDK installation. Ensure that the necessary D3DX dependencies are redistributed with any sample or with your application if it is moved to another machine.
- Be aware that replacement technologies for current uses of D3DX11 include DirectXTex and DirectXTK. D3DXMath is replaced by DirectXMath.
大致的意思就是:
在win8之后的系統沒法直接用D3DX系列的lib了,如果你想用,就要去下sdk然后按照下面的步驟改VC++目錄,且代碼里必須包含什么什么東西。
下載安裝DirectX學習模板:
地址:https://msdn.microsoft.com/zh-cn/library/windows/apps/dn166876
傻瓜教程,中文介紹,十分詳細的說了如何使用VS2013創建安裝DirectX的學習模板,創建后嘗試編譯運行,成功出來一個旋轉的有多種顏色了3D立方體就代表環境沒問題了。
編譯時候可能會需要打開windows商店的開發者模式,按他要求做就行了。
創建自己的第一個DirectX11工程:
需要一個win32項目:
VS2013----新建項目-----win32項目----空項目
修改一下項目屬性添加一些lib,當然,這一步並不是必須的,完全可以在代碼中使用#pragma comment添加,不過我習慣了自己導出模板:
項目---屬性--鏈接器---輸入(input)---附加依賴項 ,加入以下的lib:(這些都來自DirectX學習模板)
d2d1.lib
d3d11.lib
dxgi.lib
ole32.lib
windowscodecs.lib
dwrite.lib
xaudio2.lib
mfcore.lib
mfplat.lib
mfreadwrite.lib
xinput.lib
mfuuid.lib
可以導出模板方便下次使用。
然后我們需要一個最基本的windows窗體程序:
#include <Windows.h> //windows程序入口main函數 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow) { static TCHAR szAppName[] = TEXT("HelloWin"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("The Hello Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); while (TRUE) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { // z } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch (message) { case WM_CREATE: return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); DrawText(hdc, TEXT("Hello"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
代碼修改自windows程序設計,主要修改了消息循環:
原來使用GetMessage,可以理解為函數一直等待直到獲得消息,處理消息,
PeekMesage則是不斷查看消息隊列,一直返回,這樣,我們就可以在沒有消息的時候做一些別的事情了。
以下大部分資料與內容來自書籍《Introduction to 3D Game Programming with Directx 11》
以及我的一些理解,翻譯
DirectX11初始化:
初始化的過程比較復雜,主要有以下步驟:
1.Create Device and Contexrt 創建設備與上下文
D3D初始化的開始就是創建D3D11的設備與上下文,他們可以被認為是軟件操控d3d底層的主要接口,
D3d11設備的作用是檢測系統對於D3d 的支持與分配資源
上下文的作用是設置渲染,綁定視圖流水線,並發命令(這一段我翻譯的比較糟糕,原文是:The ID3D11DeviceContext interface is used to set render states, bind resources to the graphics pipeline, and issue rendering commands.)
2.創建交換鏈swapchain
為了視屏的流暢,所有2D/3D的動畫都有2個(多個?)視屏緩沖區,一個用於當前的顯示,另一個繪制下一幀,視屏每繪制一次,2者交換。原來用於繪制下一幀的緩沖區用於顯示,原來顯示的的用於繪制下一幀,接着不斷交換。
3.Create the render target view
先上原文:As said in §4.1.6, we do not bind a resource to a pipeline stage directly; instead, we must create a resource view to the resource and
bind the view to the pipeline stage. In particular, in order to bind the back buffer to the output merger stage of the pipeline (so
Direct3D can render onto it), we need to create a render target view to the back buffer. The following example code shows how this
is done:
然后是我的理解:在交換鏈中有前(front)與后(back)2個繪制緩沖區,根據書中所說:we need to create a render target view to the back buffer,這個render target view就是指向后一個繪制緩沖區,而且由於: we do not bind a resource to a pipeline stage directly,我們不能把資源直接綁定到渲染(繪制)的流水線上,所以需要一個 resource view(??)去把他綁定到渲染管道上。
4.Create the Depth/Stencil Buffer and View 創建景深與模板緩沖
景深就是在繪制的過程中,記錄圖形中每個像素的繪制深度信息,這樣在相互遮擋時就可以判斷去繪制哪個像素。
書中好像沒有stencil的介紹?模板與景深一樣記錄每一個像素點的信息,在渲染時進行一次比較,來確定是否對這個像素點進行改變。
5.Bind the view to output merger stage
把創建好的視圖綁定到渲染隊列上去。
6.SetViewPort 設置視窗
很簡單,一般來說我們的3D場景是占滿整個程序窗口的,但是,有時我們希望這個3D場景只占用程序窗口的一部分,這就需要設計視窗了。
創建的代碼:
#include <Windows.h> #include <windowsx.h> #include <d3d11.h> //D3D相關全局變量 ID3D11Device *dev = NULL; ID3D11DeviceContext *devContext = NULL; IDXGISwapChain *SwapChain = NULL; ID3D11DepthStencilView *depthStencilView = NULL; ID3D11RenderTargetView *backBuffer = NULL; float color[4] = { 0.0, 1.0, 0.0, 1.6 }; void Release() { SwapChain->SetFullscreenState(FALSE, NULL); dev->Release(); devContext->Release(); backBuffer->Release(); SwapChain->Release(); } void Render() { devContext->ClearRenderTargetView(backBuffer, reinterpret_cast<float*>(&color)); devContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0, 0); SwapChain->Present(0, 0); } BOOL CreateD3DObject(HWND hwnd) { //初始化交換鏈 /* typedef struct DXGI_SWAP_CHAIN_DESC { DXGI_MODE_DESC BufferDesc; //特指緩沖區的特性 DXGI_SAMPLE_DESC SampleDesc; //多重采樣?抗鋸齒? DXGI_USAGE BufferUsage; //對於交換鏈,為DXGI_USAGE_RENDER_TARGET_OUTPUT UINT BufferCount; //我們只創建一個后緩沖區(雙緩沖),因此為1 HWND OutputWindow; //指定窗口句柄,使用Windows窗體程序初始化完創建的主窗口句柄 BOOL Windowed; //FASE 全屏顯示,TRUE窗口顯示 DXGI_SWAP_EFFECT SwapEffect; //通常為DXGI_SWAP_EFFECT_DISCARD UINT Flags; //可選,我們設為0 } DXGI_SWAP_CHAIN_DESC; typedef struct DXGI_MODE_DESC { UINT Width; //緩沖區寬,與分辨率有關? UINT Height; //緩沖區高,與分辨率有關? DXGI_RATIONAL RefreshRate; // 其他參數一般為固定的,參見代碼。 DXGI_FORMAT Format; DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; DXGI_MODE_SCALING Scaling; } DXGI_MODE_DESC, *LPDXGI_MODE_DESC; */ DXGI_SWAP_CHAIN_DESC scd; ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC)); scd.BufferCount = 1; scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scd.BufferDesc.Width = 1200; scd.BufferDesc.Height = 600; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.OutputWindow = hwnd; scd.SampleDesc.Count = 4; scd.Windowed = TRUE; scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; /*HRESULT D3D11CreateDeviceAndSwapChain( __in IDXGIAdapter *pAdapter, //選擇圖形適配器 __in D3D_DRIVER_TYPE DriverType, //驅動類型,一般為D3D_DRIVER_TYPE_HARDWARE __in HMODULE Software, //NULL即可 __in UINT Flags, // Flags為可選參數,一般為NULL __in const D3D_FEATURE_LEVEL *pFeatureLevels, // pFeatureLevels為我們提供給程序的特征等級的一個數組,下一個參數為數組中元素個數 __in UINT FeatureLevels, __in UINT SDKVersion, // SDKVersion恆定為D3D11_SDK_VERSION; __in const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, //DXGI_SWAP_CHAIN_DESC初始化的交換鏈 __out IDXGISwapChain **ppSwapChain, //返回一個IDXGISwapChain對象的指針地址,這個交換鏈接用於渲染(二維指針) __out ID3D11Device **ppDevice, // ppDevice為設備指針的地址,注意設備是指針類型,這里傳遞的是指針的地址(二維指針,d3d程序中所有的接口都聲明為指針類型!) __out D3D_FEATURE_LEVEL *pFeatureLevel, // pFeatureLevel為最后程序選中的特征等級,我們定義相應的變量,傳遞它的地址進來; __out ID3D11DeviceContext **ppImmediateContext // pFeatureLevel為最后程序選中的特征等級,我們定義相應的變量,傳遞它的地址進來 ; 返回值 HRESULT 這個函數返回Direct3D 11返回值。 */ //創建設備與緩沖區 HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &scd, &SwapChain, &dev, NULL, &devContext); if (FAILED(hr)) { MessageBox(NULL, L"創建d3d11設備失敗!", L"錯誤", MB_OK); return FALSE; } ID3D11Texture2D *t2D; //獲取后緩沖區地址 SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&t2D); /* HRESULT CreateRenderTargetView ( ID3D11Resource *pResource, //視圖對應資源 const D3D11_RENDER_TARGET_VIEW_DESC *pDesc, //視圖描述 ID3D11RenderTargetView **ppRTView //要創建的視圖(指針的地址) ); */ //創建視圖 dev->CreateRenderTargetView(t2D, NULL, &backBuffer); //釋放后緩沖區引用 t2D->Release(); //創建深度緩沖區視圖 //要先創建對應緩沖區 //創建緩沖區Texture2D,要先給出描述 UINT msaa4xQuality(0); D3D11_TEXTURE2D_DESC desc = { 0 }; desc.Width = 1200; desc.Height = 600; desc.MipLevels = 1; desc.ArraySize = 1; desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; desc.MiscFlags = 0; desc.SampleDesc.Count = 4; desc.SampleDesc.Quality = msaa4xQuality - 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.CPUAccessFlags = 0; ID3D11Texture2D *depthStencilBuffer(NULL); hr = dev->CreateTexture2D(&desc, NULL, &depthStencilBuffer); if (FAILED(hr)) { MessageBox(NULL, L"Create d3d11 depthStencil Fail!", L"Fail", MB_OK); return FALSE; } hr = dev->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); if (FAILED(hr)) { MessageBox(NULL, L"Create DepthStencilView Fai!", L"Fail", MB_OK); return FALSE; } //有了視圖要綁定到管線相應階段 devContext->OMSetRenderTargets(1, &backBuffer, depthStencilView); depthStencilBuffer->Release(); //最后設置portview D3D11_VIEWPORT viewPort = { 0 }; viewPort.Width = static_cast<FLOAT>(1200); viewPort.Height = static_cast<FLOAT>(600); viewPort.MinDepth = 0.f; viewPort.MaxDepth = 1.f; viewPort.TopLeftX = 0.f; viewPort.TopLeftY = 0.f; devContext->RSSetViewports(1, &viewPort); return TRUE; }
//注意注意:這里有可能會一個大坑:
我的代碼中創建設備與交換鏈使用的函數是:
D3D11CreateDeviceAndSwapChain
這個函數有許多的限制:
1.據msdn的所說,這個函數無法被windows phone 8所支持。需要使用D3D11CreateDevice與CreateSwapChain先創建設備,再創建交換鏈?
2.The D3D11CreateDeviceAndSwapChain function does not exist for Windows Store apps.
它無法被windows Store App使用?,需要其它的API,具體查看,MSDN.
3.最重要的,在許多示例代碼中有這一句:
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
使用之后D3D11CreateDevice這個API在我的開發環境下的Debug模式無法正常使用,返回的錯誤原因是缺少必要的SDK組件。
解決D3D11CreateDevice Debug下失敗的方法:
以下的方法從網上收集而來,我自己沒有切實成功過:
需要安裝win8.1的SDK或者VS2012自帶了SDK,但是我的環境應該滿足這個條件啊。。。。。
看了http://www.gamedev.net/topic/670633-including-headers-from-windows-10-sdk/
並沒有解決問題,先不要這個屬性了
#include <Windows.h> //windows程序入口main函數 void Render(); void Release(); BOOL CreateD3DObject(HWND hwnd); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow) { static TCHAR szAppName[] = TEXT("HelloWin"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("The Hello Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); CreateD3DObject(hwnd); while (TRUE) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { // z Render(); } } Release(); return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch (message) { case WM_CREATE: return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); DrawText(hdc, TEXT("Hello"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }