在《Introduction to 3D G AME P ROGRAMMING WITH D IRECTX ® 11》中,关于窗口的生成,进行了封装,所以不容易理解。下边,对经典的Windows生成与本书的3D初始化做一个比较。
#include <.........h>引用头文件这里不再累述
步骤 | 功能作用 | 《Introduction to 3D G AME P ROGRAMMING WITH D IRECTX ® 11》 | 经典windows生成 | ||
子 类 | 父类(D3DApp) | 调用路径 | |||
1 | 函数的声明,参数的定义及赋值 | D3DApp::D3DApp(HINSTANCE hInstance) | 进入子类入口函数(WinMain)后,调用父类构造函数,完成函数声明、参数定义及赋值 | 在WinMain前声明函数,定义参数及赋值 | |
2 | 入口函数 | int WINAPI WinMain() | 入口函数在子类,但窗口的生成是调用父类完成的 | int WINAPI WinMain() | |
2.1 | Init() | ||||
2.1.1 | Init() | 通过子类调用父类 | |||
2.1.1.1 | 设计、创建、注册、显示及更新窗口 | 无 | InitMainWindow() | 在Init()内部调用 | 在入口函数“int WINAPI WinMain()”内完成 |
2.1.1.2 | 3D初始化 | 无 | InitDirect3D() | 在Init()内部调用 | 在入口函数“int WINAPI WinMain()”内调用 |
2.1.1.2.1 | 设置投射矩阵 | OnResize() | OnResize() | 在InitDirect3D()内首次调用,根据具体设置调用父类或子类 | 在Direct3D()内调用 |
2.1.2 | 编写图形 | BuildGeometryBuffers(); BuildFX(); BuildVertexLayout(); | 无 | 在Init()内部调用 | 在入口函数“int WINAPI WinMain()”内调用 |
2.3 | 消息循环 | 无 | Run() | 入口函数(WinMain)内调用 | 入口函数(WinMain)内运行 |
2.3.1 | virtual void UpdateScene(float dt)=0; virtual void DrawScene()=0; | 父类为纯虚函数,调用子类 | 消息循环内调用 | ||
2.3.1.1 | 设置空间绘制图形 | UpdateScene(float dt); DrawScene() | |||
3 | 回调函数 | MinWndproc调用MsgProc | 设计窗口时指定MinWndproc为回调函数,但MinWndproc内部有设定MsgProc为回调函数 | 通过设计窗口时wc.lpfnWndProc = MainWndProc; 完成指定 |
从上表可以看出(红字部分为真正执行的函数,上表为了显而易见,已将时间、贴数等函数省略,不影响比较),同经典的windows生成步骤一样
第一步:函数的声明,参数的定义及赋值,windows在入口函数前活在函数内部,而本书基本封装在了父类的构造函数(D3DApp::D3DApp(HINSTANCE hInstance))内;
第二部:窗口的设计、创建、注册、显示及更新窗口,windows在入口函数内部,而本书基本封装在了父类的InitMainWindow()内;
第三部:3D初始化,windows在入口函数内引用,而本书基本封装在了父类的InitDirect3D()内;
第四部:设置投射矩阵,OnResize(),父类子类都有作用(从下一章开始);
第五步:编写图形,子类完成,这才是真正工作的开始,体现你想编写什么;
第六步:设置空间绘制图形,在子类完成,这才是真正工作的第二部;
第七部:回调函数,重复工作,父类完成。
从以上可以看出,工作的重复部分封装在父类,一般不需要改变,个性化的部分在子类,这才是真正的工作所在。
下面,实践一下,我将第四章的封装编码去繁从简,写在了一张.cpp上,供大家分享:(注意请建立英文路径,不要带中文,否则后期,特别是读写文件时,会很麻烦。)
1.建立工程,选择:文件---新建----项目----Win32控制台应用程序-----名称自己填-----下一步-----windows应用程序 空项目------完成
2.建立cpp: 选择:源文件--新建项---C++文件----名字自己起----添加
3.(若第一次)包含路径:右击解决方案下的项目名----属性-----VC++目录---包含目录----编辑----新行(文件夹图标)----找到SDK的include--选择文件夹 再选择新行----找到本书的代码----对Common进行包含
然后选择库目录进行包含,在SDK的Lib----X86 再选择新行----找到本书的代码----对Common进行包含。
总之,在VC++中包含两个包含目录及两个库目录,共4个,已经包含过就不用了。
4.属性页不要退出,衔接器----输入----附加依赖项--复制 d3d11.lib d3dx11d.lib D3DCompiler.lib Effects11d.lib dxerr.lib
dxgi.lib dxguid.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib
oleaut32.lib uuid.lib odbc32.lib odbccp32.lib,已经有了就不用这一步了。
5.复制下列代码:
#include <windows.h>
#include <string>
#include <d3dx11.h>
#include <cassert>
#include <xnamath.h>
HINSTANCE hInstance;
HINSTANCE mhAppInst=hInstance;
int mClientWidth(800);
int mClientHeight(600);
HWND mhMainWnd=0;
std::wstring mMainWndCaption(L"D3D11 Application");
D3D_DRIVER_TYPE md3dDriverType(D3D_DRIVER_TYPE_HARDWARE);
ID3D11Device* md3dDevice(0);
ID3D11DeviceContext* md3dImmediateContext(0);
UINT m4xMsaaQuality(0);
bool mEnable4xMsaa(false);
IDXGISwapChain* mSwapChain(0);
ID3D11RenderTargetView* mRenderTargetView=0;
ID3D11DepthStencilView* mDepthStencilView=0;
ID3D11Texture2D* mDepthStencilBuffer=0;
D3D11_VIEWPORT mScreenViewport;
namespace Colors
{
XMGLOBALCONST XMVECTORF32 White = {1.0f, 1.0f, 1.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Black = {0.0f, 0.0f, 0.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Red = {1.0f, 0.0f, 0.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Green = {0.0f, 1.0f, 0.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Blue = {0.0f, 0.0f, 1.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Yellow = {1.0f, 1.0f, 0.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Cyan = {0.0f, 1.0f, 1.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Magenta = {1.0f, 0.0f, 1.0f, 1.0f};
XMGLOBALCONST XMVECTORF32 Silver = {0.75f, 0.75f, 0.75f, 1.0f};
XMGLOBALCONST XMVECTORF32 LightSteelBlue = {0.69f, 0.77f, 0.87f, 1.0f};
}
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool InitDirect3D();
void DrawScene();
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd)
{
ZeroMemory(&mScreenViewport, sizeof(D3D11_VIEWPORT));
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mhAppInst;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"D3DWndClassName";
if( !RegisterClass(&wc) )
{
MessageBox(0, L"RegisterClass Failed.", 0, 0);
return false;
}
RECT R = { 0, 0, mClientWidth, mClientHeight };
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
int width = R.right - R.left;
int height = R.bottom - R.top;
mhMainWnd = CreateWindow(L"D3DWndClassName", mMainWndCaption.c_str(),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, mhAppInst, 0);
if( !mhMainWnd )
{
MessageBox(0, L"CreateWindow Failed.", 0, 0);
return false;
}
ShowWindow(mhMainWnd, SW_SHOW);
UpdateWindow(mhMainWnd);
InitDirect3D();
MSG msg = {0};
while(msg.message != WM_QUIT)
{
// If there are Window messages then process them.
if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// Otherwise, do animation/game stuff.
else DrawScene();
}
return 0;
}
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
// WM_ACTIVATE is sent when the window is activated or deactivated.
// We pause the game when the window is deactivated and unpause it
// when it becomes active.
case WM_ACTIVATE:
return 0;
// WM_SIZE is sent when the user resizes the window.
case WM_SIZE:
// Save the new client area dimensions.
mClientWidth = LOWORD(lParam);
mClientHeight = HIWORD(lParam);
return 0;
// WM_EXITSIZEMOVE is sent when the user grabs the resize bars.
// WM_DESTROY is sent when the window is being destroyed.
case WM_DESTROY:
PostQuitMessage(0);
return 0;
// The WM_MENUCHAR message is sent when a menu is active and the user presses
// a key that does not correspond to any mnemonic or accelerator key.
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
bool InitDirect3D()
{
UINT createDeviceFlags = 0;
D3D_FEATURE_LEVEL featureLevel;
HRESULT hr = D3D11CreateDevice(
0, // default adapter
md3dDriverType,
0, // no software device
createDeviceFlags,
0, 0, // default feature level array
D3D11_SDK_VERSION,
&md3dDevice,
&featureLevel,
&md3dImmediateContext);
if( FAILED(hr) )
{
MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);
return false;
}
if( featureLevel != D3D_FEATURE_LEVEL_11_0 )
{
MessageBox(0, L"Direct3D Feature Level 11 unsupported.", 0, 0);
return false;
}
md3dDevice->CheckMultisampleQualityLevels(
DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m4xMsaaQuality);
assert( m4xMsaaQuality > 0 );
DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mClientWidth;
sd.BufferDesc.Height = mClientHeight;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
if( mEnable4xMsaa )
{
sd.SampleDesc.Count = 4;
sd.SampleDesc.Quality = m4xMsaaQuality-1;
}
// No MSAA
else
{
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
}
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = mhMainWnd;
sd.Windowed = true;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags =DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
IDXGIDevice* dxgiDevice = 0;
md3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
IDXGIAdapter* dxgiAdapter = 0;
dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter);
IDXGIFactory* dxgiFactory = 0;
dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory);
dxgiFactory->CreateSwapChain(md3dDevice, &sd, &mSwapChain);
if(dxgiDevice) dxgiDevice=0;
if(dxgiAdapter) dxgiAdapter=0;
if(dxgiFactory) dxgiFactory=0;
assert(md3dImmediateContext);
assert(md3dDevice);
assert(mSwapChain);
if(mRenderTargetView) mRenderTargetView=0;
if(mDepthStencilView) mDepthStencilView=0;
if(mDepthStencilBuffer) mDepthStencilBuffer=0;
mSwapChain->ResizeBuffers(1, mClientWidth, mClientHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
ID3D11Texture2D* backBuffer;
mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer));
md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView);
if(backBuffer) backBuffer=0;
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
if( mEnable4xMsaa )
{
depthStencilDesc.SampleDesc.Count = 4;
depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality-1;
}
// No MSAA
else
{
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
}
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer);
md3dDevice->CreateDepthStencilView(mDepthStencilBuffer, 0, &mDepthStencilView);
md3dImmediateContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);
mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width = static_cast<float>(mClientWidth);
mScreenViewport.Height = static_cast<float>(mClientHeight);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;
md3dImmediateContext->RSSetViewports(1, &mScreenViewport);
return true;
}
void DrawScene()
{
assert(md3dImmediateContext);
assert(mSwapChain);
md3dImmediateContext->ClearRenderTargetView(mRenderTargetView, reinterpret_cast<const float*>(&Colors::Blue));
md3dImmediateContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
mSwapChain->Present(0, 0);
}
结果展示: