《Beginning DirectX11 Game Programming》读书笔记2 一步一步开始第一个DirectX11程序
最好的学习方式就是动手操作。以下代码可以在书籍官方网站上下载。
以下基于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渲染实现。