来学习一下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); }