在DirectX程序中我们可以设置多个不同的视口用于显示不同的图形,每个视口都可以清空和独立绘制。渲染目标表面的深度值范围(通常是0.0到 1.0),在这之间的物体会被渲染。

我们先看以下处理多个视口的代码:

//********************************************************************
//      Filename: Main.cpp
//        Author: Chinafish
//      Modifier: Chinafish
//       Created: 2010-06-25 12:16
//       Updated: 2010-06-25 12:16
//            QQ: 149200849
//           MSN: china_fish@msn.com
//       Purpose: Set multiple view ports.
//====================================================================
//  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 - Multiple Viewports (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;

DWORD					g_dwBackBufferWidth  = 0;
DWORD					g_dwBackBufferHeight = 0;
//-----------------------------------------------------------------------------
// 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_MULTIPLEVIEWPORTS";
	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_MULTIPLEVIEWPORTS",
		WINDOW_TITLE,
		WS_EX_TOPMOST, 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_MULTIPLEVIEWPORTS", 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_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;
	}

	//
	// Cache the width & height of the back-buffer...
	//

	LPDIRECT3DSURFACE9 pBackBuffer = NULL;
	D3DSURFACE_DESC d3dsd;
	g_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	pBackBuffer->GetDesc(&d3dsd);
	pBackBuffer->Release();
	g_dwBackBufferWidth  = d3dsd.Width;
	g_dwBackBufferHeight = d3dsd.Height;

	// 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)
{
	//
	// Render to the Top-left viewport
	//

	D3DVIEWPORT9 tlViewPort;
	tlViewPort.X      = 0;
	tlViewPort.Y      = 0;
	tlViewPort.Width  = g_dwBackBufferWidth / 2;
	tlViewPort.Height = g_dwBackBufferHeight / 2;
	tlViewPort.MinZ   = 0.0f;
	tlViewPort.MaxZ   = 1.0f;

	g_pd3dDevice->SetViewport(&tlViewPort);

	// Clear screen with D3DCOLOR_XRGB(255, 0, 0)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0);

	g_pd3dDevice->BeginScene();

	// Render geometry here...

	g_pd3dDevice->EndScene();

	//
	// Render to the Top-right viewport
	//

	D3DVIEWPORT9 trViewPort;
	trViewPort.X      = g_dwBackBufferWidth / 2;
	trViewPort.Y      = 0;
	trViewPort.Width  = g_dwBackBufferWidth / 2;
	trViewPort.Height = g_dwBackBufferHeight / 2;
	trViewPort.MinZ   = 0.0f;
	trViewPort.MaxZ   = 1.0f;

	g_pd3dDevice->SetViewport(&trViewPort);

	// Clear screen with D3DCOLOR_XRGB(0, 255, 0)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0);

	g_pd3dDevice->BeginScene();

	// Render geometry here...

	g_pd3dDevice->EndScene();

	//
	// Render to the Bottom-left viewport
	//

	D3DVIEWPORT9 blViewPort;
	blViewPort.X      = 0;
	blViewPort.Y      = g_dwBackBufferHeight / 2;
	blViewPort.Width  = g_dwBackBufferWidth / 2;
	blViewPort.Height = g_dwBackBufferHeight / 2;
	blViewPort.MinZ   = 0.0f;
	blViewPort.MaxZ   = 1.0f;

	g_pd3dDevice->SetViewport(&blViewPort);

	// Clear screen with D3DCOLOR_XRGB(0, 0, 255)
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);

	g_pd3dDevice->BeginScene();

	// Render geometry here...

	g_pd3dDevice->EndScene();

	//
	// Render to the Bottom-right viewport
	//

	D3DVIEWPORT9 brViewPort;
	brViewPort.X      = g_dwBackBufferWidth / 2;
	brViewPort.Y      = g_dwBackBufferHeight / 2;
	brViewPort.Width  = g_dwBackBufferWidth / 2;
	brViewPort.Height = g_dwBackBufferHeight / 2;
	brViewPort.MinZ   = 0.0f;
	brViewPort.MaxZ   = 1.0f;

	g_pd3dDevice->SetViewport(&brViewPort);

	// 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);
}
				

框架代码我们在前面已经有说明,这里需要说明的是在创建设备后取到后台缓冲大小,在渲染时调用 IDirect3DDevice9::SetViewport 方法来设置四个不同的视口并在其中使用不同的颜色填充。

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

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