在DirectX程序中当我们改变窗口大小时,需要重新设置后台缓冲大小和重置设备,如果不这样操作,以新的窗口大小绘制图形将会出现变形。

我们先看以下处理改变窗口大小的代码:

//********************************************************************
//      Filename: Main.cpp
//        Author: Chinafish
//      Modifier: Chinafish
//       Created: 2010-06-25 11:52
//       Updated: 2010-06-25 11:52
//            QQ: 149200849
//           MSN: china_fish@msn.com
//       Purpose: Resize dx window.
//====================================================================
//  Copyright(C) 2004-2010 by Chinafish. All Rights Reserved.
//********************************************************************

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include "windows.h"

// Link D3D
#include "d3d9.h"
#pragma comment(lib, "d3d9.lib")

#define Release_Safe(p){if(p!=NULL) p->Release();}

#define WINDOW_TITLE    "www.csinx.org - Resize Window (DX9+VC7)"
#define WINDOW_WIDTH	800
#define WINDOW_HEIGHT	600

//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------
HWND					g_hWnd			= NULL;
LPDIRECT3D9				g_pD3D			= NULL;
LPDIRECT3DDEVICE9		g_pd3dDevice	= NULL;
D3DPRESENT_PARAMETERS	g_d3dpp;

//-----------------------------------------------------------------------------
// PROTOTYPES
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void InitD3D();
HRESULT RestoreDeviceObjects();
HRESULT InvalidateDeviceObjects();
void ShutDown(void);
void Render(void);

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX winClass;
	MSG        uMsg;
	memset(&uMsg,0,sizeof(uMsg));

	winClass.lpszClassName = "CLASS_RESIZEWINDOW";
	winClass.cbSize        = sizeof(WNDCLASSEX);
	winClass.style         = CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc   = WindowProc;
	winClass.hInstance     = hInstance;
	winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)NULL);
	winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)NULL);
	winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName  = NULL;
	winClass.cbClsExtra    = 0;
	winClass.cbWndExtra    = 0;

	if(!RegisterClassEx(&winClass))
		return E_FAIL;

	g_hWnd = CreateWindowEx(NULL, "CLASS_RESIZEWINDOW",
		WINDOW_TITLE,
		WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0,
		WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL,
		hInstance, NULL);

	if(g_hWnd == NULL)
		return E_FAIL;

	// Adjust window
	RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
	AdjustWindowRect(&rect, GetWindowLong(g_hWnd, GWL_STYLE), FALSE);
	SetWindowPos(g_hWnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
		SWP_NOZORDER | SWP_NOMOVE);

	ShowWindow(g_hWnd, nCmdShow);
	UpdateWindow(g_hWnd);

	// Init or reset D3D
	InitD3D();

	while(uMsg.message != WM_QUIT)
	{
		if(PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&uMsg);
			DispatchMessage(&uMsg);
		}
		else
		{
			// Render a frame
			Render();
		}
	}

	ShutDown();

	UnregisterClass("CLASS_RESIZEWINDOW", winClass.hInstance);

	return uMsg.wParam;
}

//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_KEYDOWN:
		{
			switch(wParam)
			{
			case VK_ESCAPE:
				PostQuitMessage(0);
				break;
			}
		}
		break;
	case WM_SIZE:
		{
			//
			// If the device is not NULL and the WM_SIZE message is not a
			// SIZE_MINIMIZED event, resize the device's swap buffers to match
			// the new window size.
			//
			if(g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
			{
				InvalidateDeviceObjects();

				g_d3dpp.BackBufferWidth  = LOWORD(lParam);
				g_d3dpp.BackBufferHeight = HIWORD(lParam);

				HRESULT hr = g_pd3dDevice->Reset(&g_d3dpp);

				if(hr == D3DERR_INVALIDCALL)
				{
					MessageBox(NULL, "Reset() - Failed!", "ERROR", NULL);
				}

				RestoreDeviceObjects();
			}
		}
		break;
	case WM_CLOSE:
		{
			PostQuitMessage(0);
		}
	case WM_DESTROY:
		{
			PostQuitMessage(0);
		}
		break;
	default:
	{
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	break;
	}

	return 0;
}

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: This function will only be called once during the application's
//       initialization phase. Therefore, it can't contain any resources that
//       need to be restored every time the Direct3D device is lost or the
//       window is resized.
//-----------------------------------------------------------------------------
void InitD3D()
{
	// Create D3D
	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if(!g_pD3D)
	{
		MessageBox(NULL, "Direct3DCreate9() - Failed!", "Error", NULL);
		return;
	}

	D3DDISPLAYMODE d3ddm;
	g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

	ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
	g_d3dpp.Windowed               = TRUE;
	g_d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
	g_d3dpp.BackBufferFormat       = d3ddm.Format;
	g_d3dpp.EnableAutoDepthStencil = TRUE;
	g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	g_d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;

	g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&g_d3dpp, &g_pd3dDevice);

	if(!g_pd3dDevice)
	{
		return;
	}

	// Any resources or settings that need to be restored after losing the
	// DirectX device should probably be grouped together into one function so
	// they can be re-created or reset in one call.
	RestoreDeviceObjects();
}

//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc: You are encouraged to develop applications with a single code path to
//       respond to device loss. This code path is likely to be similar, if not
//       identical, to the code path taken to initialize the device at startup.
//-----------------------------------------------------------------------------
HRESULT RestoreDeviceObjects()
{
	// Set some important state settings...
	// Set Transform
	// Create a texture object, mesh object, buffer etc.
	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InvalidateDeviceObjects()
// Desc: If the lost device can be restored, the application prepares the
//       device by destroying all video-memory resources and any
//       swap chains. This is typically accomplished by using the SAFE_RELEASE
//       macro.
//-----------------------------------------------------------------------------
HRESULT InvalidateDeviceObjects()
{
	// Invalidate the texture object, mesh object, buffer etc.

	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: ShutDown()
// Desc: Release all
//-----------------------------------------------------------------------------
void ShutDown(void)
{
	InvalidateDeviceObjects();

	Release_Safe(g_pd3dDevice);
	Release_Safe(g_pD3D);
}

//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Render something you want
//-----------------------------------------------------------------------------
void Render(void)
{
	// Clear screen with D3DCOLOR_XRGB(255, 0, 255)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 255), 1.0f, 0);

	g_pd3dDevice->BeginScene();

	// Render geometry here...

	g_pd3dDevice->EndScene();

	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}
				

框架代码我们在前面已经有说明,这里需要说明的是在收到窗口改变消息(WM_SIZE)时如果设备不为空且不是最小化则销毁所有已调用的显存资源和交换链,然后重新设置后台缓冲大小为改变后的窗口大小再调用 IDirect3DDevice9::Reset 方法重置设备,操作完成后再恢复纹理,顶点数据等。

下图为本例代码执行效果:

[返回目录][我有话说]