在以前的例子中我们只是简单的创建D3D程序, 并使用CPU代替GPU做了很多的图形计算, 现在GPU的图形处理能力远远超过CPU, 我们应该使用GPU做图形渲染的运算, 而让CPU有更多的时间处理其他的事情如进行逻辑计算等. 通常我们的程序需要运行在不同硬件配置的电脑上, 用户也要选择不同的分辩率, 刷新率和颜色等, 这样就要检查硬件配置是否支持该模式或仅列出可用的模式. 以下为可检查硬件是否支持硬件加速等能力, 你可以在程序中配置分辩率和显示模式等.

//********************************************************************
//      Filename: Main.cpp
//        Author: Chinafish
//      Modifier: Chinafish
//       Created: 2008-3-18 10:56
//       Updated: 2008-3-18 10:56
//            QQ: 149200849
//           MSN: china_fish@msn.com
//       Purpose: Initialize improved D3D.
//====================================================================
//  Copyright(C) 2004-2008 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 - Initializing improved D3D (DX9+VC7)"
#define WINDOW_WIDTH	800 
#define WINDOW_HEIGHT	600 
#define BEWINDOWED		TRUE    // use window or fullscreen 

//----------------------------------------------------------------------------- 
// GLOBALS 
//----------------------------------------------------------------------------- 
HWND              g_hWnd       = NULL; 
LPDIRECT3D9       g_pD3D       = NULL; 
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; 
 
//----------------------------------------------------------------------------- 
// PROTOTYPES 
//----------------------------------------------------------------------------- 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow); 
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 
BOOL InitD3D(void); 
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_IMPROVED_D3D"; 
	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_IMPROVED_D3D", 
		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 D3D 
	if(!InitD3D())
		return FALSE;

	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_IMPROVED_D3D", 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: Initialize Direct 3D
//-----------------------------------------------------------------------------
BOOL InitD3D( void )
{
	// Create D3D
	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if(!g_pD3D)
	{
		MessageBox(NULL, "Direct3DCreate9() - Failed!", "Error", NULL);
		return FALSE;
	}
	
	//
	// For the default adapter, examine all of its display modes to see if any
	// of them can give us the hardware support we desire.
	//
	
	D3DDISPLAYMODE d3ddm;
	bool bDesiredAdapterModeFound = false;
	int nMaxAdapterModes = g_pD3D->GetAdapterModeCount( D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8 );
	for( int nMode = 0; nMode < nMaxAdapterModes; ++nMode )
	{
		if( FAILED( g_pD3D->EnumAdapterModes( D3DADAPTER_DEFAULT,
			D3DFMT_X8R8G8B8, nMode, &d3ddm ) ) )
		{
			MessageBox(NULL, "EnumAdapterModes() - Failed!", "Error", NULL);
			return FALSE;
		}
		
		// Does this adapter support a mode of WINDOW_WIDTH x WINDOW_HEIGHT?
		if( d3ddm.Width != WINDOW_WIDTH || d3ddm.Height != WINDOW_HEIGHT )
			continue;
		
		// Does this adapter support a 32-bit RGB pixel format?
		if( d3ddm.Format != D3DFMT_X8R8G8B8 )
			continue;
		
		// Does this adapter support a refresh rate of 75 MHz?
		if( d3ddm.RefreshRate != 75 )
			continue;
		
		// We found a match!
		bDesiredAdapterModeFound = true;
		break;
	}

	if( bDesiredAdapterModeFound == false )
	{
		MessageBox(NULL, "Get desired adapter mode - Failed!", "Error", NULL);
		return FALSE;
	}
	
	// Does this adapter support hardware vertex processing?
	D3DCAPS9 d3dCaps;
	if(FAILED(g_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps )))
	{
		MessageBox(NULL, "GetDeviceCaps() - Failed!", "Error", NULL);
		return FALSE;
	}
	
	DWORD dwVertexProcessing = 0;
	if ( d3dCaps.VertexProcessingCaps != 0 )
		dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	
	// Does this adapter support 32-bit back buffer?
	D3DFORMAT stencilFormat;
	D3DFORMAT adapterFormat = (BEWINDOWED) ? d3ddm.Format : D3DFMT_X8R8G8B8;
	if( FAILED( g_pD3D->CheckDeviceType( D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,
		adapterFormat,D3DFMT_X8R8G8B8,
		BEWINDOWED ) ) )
	{
		MessageBox(NULL, "Get a 32-bit back buffer - Failed!", "Error", NULL);
		return FALSE;
	}
	
	// Does this adapter support DepthStencil(at least need a 16-bit z-buffer)?
	if ( SUCCEEDED( g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		adapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24S8 ) ) )
	{
		stencilFormat = D3DFMT_D24S8;
	}
	else if ( SUCCEEDED( g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		adapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X8 ) ) )
	{
		stencilFormat = D3DFMT_D24X8;
	}
	else if ( SUCCEEDED( g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		adapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )
	{
		stencilFormat = D3DFMT_D16;
	}
	else
	{
		MessageBox(NULL, "Find a valid DepthStencil Format - Failed!", "Error", NULL);
		return FALSE;
	}
	
	// Fill parameters
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.BackBufferWidth				= (BEWINDOWED) ? 0 : d3ddm.Width;
	d3dpp.BackBufferHeight				= (BEWINDOWED) ? 0 : d3ddm.Height;
	d3dpp.BackBufferFormat				= adapterFormat;
	d3dpp.BackBufferCount				= 1;
	d3dpp.MultiSampleType				= D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality			= 0;
	d3dpp.SwapEffect					= D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow					= g_hWnd;
	d3dpp.Windowed						= BEWINDOWED;
	d3dpp.EnableAutoDepthStencil		= TRUE;
	d3dpp.AutoDepthStencilFormat		= stencilFormat;
	d3dpp.FullScreen_RefreshRateInHz	= (BEWINDOWED) ? 0 : d3ddm.RefreshRate;
	d3dpp.PresentationInterval			= D3DPRESENT_INTERVAL_IMMEDIATE;
	
	// Create D3D device
	g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
		g_hWnd, dwVertexProcessing, &d3dpp, &g_pd3dDevice);
	if(!g_pd3dDevice)
	{
		MessageBox(NULL, "CreateDevice() - Failed!", "Error", NULL);
		return FALSE;
	}
	
	return TRUE; 
}

//-----------------------------------------------------------------------------
// Name: ShutDown()
// Desc: Release all
//-----------------------------------------------------------------------------
void ShutDown( void )
{
	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 | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 0, 255), 1.0f, 0 );
	
	g_pd3dDevice->BeginScene();
	
	// Render geometry here...
	g_pd3dDevice->EndScene();
	
	g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
			

在以上代码中我们在错误的时候通过 MessageBox 弹出消息对话框显示出错的信息.

首先在我们通过 Direct3DCreate9 创建一个 D3D物体 , 成功后使用 IDirect3D9::GetAdapterModeCount 得到默认显卡支持的 显示模式 数量, 然后通过 IDirect3D9::EnumAdapterModes 检查32位颜色和75MHz刷新率下是否有我们想要使用的分辩率, 当然你也可以设置这些. 当我们检查硬件可以得到想要的 显示模式 后再通过调用 IDirect3D9::GetDeviceCaps 来检查是否支持硬件加速顶点处理器, 该函数将填充 D3DCAPS9 结构, 我们还可以获取硬件支持的VS和PS的版本等信息. 通过 IDirect3D9::CheckDeviceType 来检查是否支持32位的后置缓冲区, 这里其实我们只需要检查全屏模式. 通过 IDirect3D9::CheckDeviceFormat 来检查硬件支持的深度&模板缓冲格式, 深度缓冲也称Z缓冲或W缓冲, 用于存储深度信息, 深度信息决定光栅化时每个三角形的相互遮蔽, 如果启用深度缓冲, 在光栅化三维场景时渲染表面的每个点都要被测试. 深度缓冲中的值可以是一个点的Z坐标值或它的齐次坐标W值, 大多的驱动程序支持基于Z值的深度缓冲但不一定支持基于W值的深度缓冲, 如果试图启用不支持的类型, 驱动程序不会失败而是退而使用另一种缓冲类型或是完全禁用深度缓冲, 这将导致渲染的场景内有极多的深度排序遗留物体. 在这里我们检查硬件是否支持最低16位的深度缓冲.

最后通过获取的参数填充 D3DPRESENT_PARAMETERS 结构, 在创建全屏模式或窗口模式时使用不同的参数.

设置显示参数以后我们通过 IDirect3D9::CreateDevice 来创建一个 D3D设备 . 现在我们已经完成DirectX图形的初始化并可以使用D3D渲染啦.

我们在渲染时需要通过 IDirect3DDevice9::Clear 清屏(这里要注意的是我们使用了深度缓冲, 清屏时除了清 D3DCLEAR_TARGET 还要清 D3DCLEAR_ZBUFFER)并指定一个颜色填充后置缓冲区(我们需要用来画东西的表面), D3DCOLOR_XRGB 宏用于指定一种颜色. 需要注意的是我们在渲染任何物体之前都要调用 IDirect3DDevice9::BeginScene 函数并且在结束渲染后调用 IDirect3DDevice9::EndScene . 这主要是告诉DirectX我们需要画一些图形并且已经清过后置缓冲区啦. 最后我们通过调用 IDirect3DDevice9::Present 切换后置缓冲区到前置缓冲区, 将我们画的图形显示到屏幕上.

D3D物体D3D设备 都是 COM物体 , 我们需要在程序结束后通过 Release 将资源释放给Window, 在以上代码中我们使用自定义的Release_Safe宏释放资源.

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

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