Getting Started with DXUT(DXUT 进阶)
开始一个工程基于DXUT:
新建立一个window应用程序——>>>>>然后把DXUT.cpp,DXUT.h,DXUTenum.cpp,DXUTenum.h等等包含在
建立的项目中,然后新建一个main.cpp就可以使用DXUT了。DXUT主要是对Directx的一次封装。方便一些工具,游戏
模拟软件的快速开发。注意,要把库链接上(dxerr.lib dxguid.lib d3dx9d.lib d3d9.lib d3dx10d.lib winmm.lib comctl32.lib)
还有就是项目属性—->>>链接器—>>输入———->>>延迟加载的DLL设置为d3dx10d_34.dll,否则会
链接运行不起。
初始化 DXUT
通过调用 DXUTInit函数来初始化
HRESULT WINAPI DXUTInit(
bool bParseCommandLine = true,
bool bShowMsgBoxOnError = true,
WCHAR* strExtraCommandLineParams = NULL,
bool bThreadSafeDXUT = false );
你可以在WinMain函数开始时就初始化,如果不调用此函数也不所谓,DXUT将自动为你设置一个默认的参数。
bParaseCommandLine参数就是表示是否分析命令行参数。如BasicHLSL Sample例子可以通过
BasicHLSL.exe -windowed -width:400 -height:300来为应用程序进行初始化
bShowMsgBoxOnError参数很显然表示是否显示错误提示对话框,当DXUT检测到错误是否弹出
strExtraCommandLineParams参数是为命令行附上的额外参数,如果为NULL就附加
bThreadSafeDXUT参数就是当重新获得一个状态,或者修改状态DXUT将进入一段临界区域。大多应用程序都设置为false,因为都没有多线程。
创建一个窗口
每一个D3D应用程序都是通过桌面上的一个窗体来与用户交流。
你可以通过DXUT来创建一个窗体,也可以自己调用window API来创建一个窗体。
通过DXUT来创建窗体(By Windows)
DXUT是创建窗体非常容易,只需要调用(DXUTCreateWindow)
HRESULT DXUTCreateWindow(
const WCHAR *strWindowTitle = L"Direct3D Window",
HINSTANCE hInstance = NULL,
HICON hIcon = NULL,
HMENU hMenu = NULL,
INT x = CW_USEDEFAULT,
INT y = CW_USEDEFAULT
);
所有的输入参数都是可选的.
.strWindowTitle 是窗口的标题栏显示的文字,还有window任务栏显示的文字
.hInstance是应用程序的实例句柄。大多数应用程序都是NULL
.hIcon 是应用程序的图标,如果为NULL将使用系统默认的
.hMenu是菜单句柄。大多数游戏都不使用系统的菜单,都是自己定义用户界面
.(x,y)指定窗口的位置(只有窗口模式有用);全屏模式的话这两个参数被忽略
如果通过DXUTCreateWindow后,可以通过 DXUTGetHWND来获得窗口的句柄HWND
一旦窗口创建完毕后,就可以通过DXUTSetWindow来初始化窗口
HRESULT DXUTSetWindow(
HWND hWndFocus,
HWND hWndDeviceFullScreen,
HWND hWndDeviceWindowed,
BOOL bHandleMessages = TRUE
);
这个函数有三个窗口句柄,如果你在全屏和窗口模式不使用一个窗口的话其实他们都是一个窗口句柄。
处理窗口事件
当我们把窗口创建好后我们还需要为窗口写消息回调函数,这样当消息发生才有地方处理。
当通过DXUTCreateWindow创建窗口,注意是DXUTCreateWindow哈,呵呵…
这个函数创建的后,一些重要的窗口消息都自动处理了。
如果处理除了DXUT处理以外的窗口事件,可以调用DXUTSetCallbackMsgProc来设置一个回调函数,他将会
调用在DXUT默认的回调处理函数(LPDXUTCALLBACKMSGPROC)之前调用
LRESULT CALLBACK MsgProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
bool* pbNoFurtherProcessing,
void* pUserContext )
{
return 0;
}
创建一个D3D设备
一旦你已经 初始化DXUT 和 创建一个窗体后,下一步就是创建一个渲染设备。你可能直接用Direct3D 9或10 API调用,或者可以
直接使用DXUT来简化设备的创建过程。
为你应用程序创建一个渲染设备
如果你创建设备直接用Directx API,你可以调用如下函数:
Direct3D 9 用 IDirect3D9::CreateDevice
Direct3D 10 用 D3D10CreateDevice
这两个函数都需要很多参数,比如:一个适配器,设备类型,一个应用程序句柄,软/硬件顶点处理,还有其他驱动信息,渲染参数
渲染参数又包含很多选项要指定:如后绘面缓存,交换方式,深度缓存等等。
使用DXUT来创建,可以简化这一步骤,当然只是把一些我们不关心的参数给了默认设置即可。
通过DXUT来创建一个渲染设备:
你一个简单调用DXUTCreateDevice来创建一个设备
HRESULT WINAPI DXUTCreateDevice(
bool bWindowed = true,
int nSuggestedWidth = 0,
int nSuggestedHeight = 0 );
其他参数都是默认设置了,这些默认值是什么你如果想知道可以跟踪一下源代码,或者参看MSDN
通过 IsDeviceAcceptable 回调函数来自定义渲染设备的参数
为了帮助DXUT选择最好的设备参数设置,我们要么通过 DXUTSetCallbackD3D9DeviceAcceptable或者DXUTSetCallbackD3D10DeviceAcceptable来
指定回调函数,这个毁掉函数将过滤掉你的机器不适合的设置。不支持就返回一个false,例如下面
bool CALLBACK IsD3D9DeviceAcceptable(
D3DCAPS9* pCaps,
D3DFORMAT AdapterFormat,
D3DFORMAT BackBufferFormat,
bool bWindowed )
{
if( BackBufferFormat == D3DFMT_X1R5G5B5 || BackBufferFormat == D3DFMT_R5G6B5 )
return false;
if( pCaps->PixelShaderVersion < D3DPS_VERSION(2,0) )
return false;
return true;
}
不支持16位后绘缓存格式和像素着色器模式2
通过ModifyDeviceSetting回调函数来改变一个渲染设备的设置
在选择了最好的设置以后,我们需要修改设备时候,可以通过ModifyDeviceSettings回调函数。这个回调函数
带上一个DXUTDeviceSetting结构体。这个结构体是DXUTD3D10DeviceSettings和DXUTD3D9DeviceSettings的一个联合体
它包含任何创建设备的信息。DXUTDeviceSettings结构体里有一个ver属性,它表示设备是9还是10,开始时候
结构体被填充了默认的值,然后我们只需要在回调函数里调用IDirect3D9或10的方法来改变具体的参数,例如下面:
bool CALLBACK ModifyDeviceSettings(
DXUTDeviceSettings* pDeviceSettings,
void* pUserContext )
{
if( pDeviceSettings->ver == DXUT_D3D9_DEVICE )
{
IDirect3D9* pD3D = DXUTGetD3DObject();
if( SUCCEEDED( pD3D->CheckDeviceFormat(
pDeviceSettings->d3d9.AdapterOrdinal, pDeviceSettings->d3d9.DeviceType,
pDeviceSettings->d3d9.AdapterFormat, D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE, D3DFMT_D24S8 ) ) )
{
if( SUCCEEDED( pD3D->CheckDepthStencilMatch(
pDeviceSettings->d3d9.AdapterOrdinal, pDeviceSettings->d3d9.DeviceType,
pDeviceSettings->d3d9.AdapterFormat, pDeviceSettings->d3d9.pp.BackBufferFormat,
D3DFMT_D24S8 ) ) )
{
pDeviceSettings->d3d9.pp.AutoDepthStencilFormat = D3DFMT_D24S8;
}
}
}
return true;
}
也可以使用 DXUT 里的 CD3D9Enumeration来检查格式是否支持
bool CALLBACK ModifyDeviceSettings(
DXUTDeviceSettings* pDeviceSettings,
void* pUserContext )
{
if( pDeviceSettings->ver == DXUT_D3D9_DEVICE )
{
CD3D9Enumeration *pEnum = DXUTGetD3D9Enumeration();
CD3D9EnumDeviceSettingsCombo *pCombo;
pCombo = pEnum->GetDeviceSettingsCombo( pDeviceSettings );
if( pCombo->depthStencilFormatList.Contains( D3DFMT_D24S8 ) )
pDeviceSettings->d3d9.pp.AutoDepthStencilFormat = D3DFMT_D24S8;
}
return true;
}
对硬件的顶点着色器版本的检查,看他是否支持指定版本。也是通过ModifyDeviceSetting进行验证
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings,
void* pUserContext )
{
if( pDeviceSettings->ver == DXUT_D3D9_DEVICE )
{
D3DCAPS9 caps;
DXUTGetD3D9DeviceCaps( pDeviceSettings, &caps );
// If device doesn’t support HW T&L or doesn’t support 1.1 vertex
// shaders in HW, then switch to SWVP.
if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )
{
pDeviceSettings->d3d9.BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
else
{
pDeviceSettings->d3d9.BehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
}
return true;
}
创建主循环
当你 初始化DXUT—>>>创建一个窗口——>>>创建一个设备 后,下一步就是执行主循环(也可以叫做 渲染循环 和 消息循环 )。
这个主循环 响应设备事件 窗口消息,然后更新场景 渲染场景。
普通的主循环:
在DXUT里面提供了最容易的方法来创建主循环。通过调用 DXUTMainLoop来实现就可以了。
当这个函数调用过后,当应用程序关闭才返回。应用程序会注册设备事件毁掉,和消息响应。
自定义的主循环:
自定义一个主循环是写应用程序比较好的选择。下面是自定义的一个消息循环
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, INT )
{
DXUTSetCallbackD3D9DeviceAcceptable( IsDeviceAcceptable );
DXUTSetCallbackD3D9DeviceCreated( OnCreateDevice );
DXUTSetCallbackD3D9DeviceReset( OnResetDevice );
DXUTSetCallbackD3D9FrameRender( OnFrameRender );
DXUTSetCallbackD3D9DeviceLost( OnLostDevice );
DXUTSetCallbackD3D9DeviceDestroyed( OnDestroyDevice );
DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackKeyboard( KeyboardProc );
DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
DXUTInit( true, true );
DXUTCreateWindow( L"Example" );
DXUTCreateDevice( true, 640, 480 );
// Custom main loop
HWND hWnd = DXUTGetHWND();
BOOL bGotMsg;
MSG msg;
msg.message = WM_NULL;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
while( WM_QUIT != msg.message )
{
// Use PeekMessage() so we can use idle time to render the scene
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
if( bGotMsg )
{
// Translate and dispatch the message
if( 0 == TranslateAccelerator( hWnd, NULL, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
// Render a frame during idle time (no messages are waiting)
DXUTRender3DEnvironment();
}
}
return DXUTGetExitCode();
}
这个例子调用 DXUTRender3DEnvironment 函数来实现 场景的更新渲染和设备事件的处理。当然你也可以完全不用
这个函数,自己处理但不推荐。
处理事件:
一旦你 初始了DXUT—–>>>>>创建了窗口——>>>创建了渲染设备——->>>创建了主循环。下一步就是处理消息
DXUT 使用一个回调函数机制来响应事件。应用程序需要注册一系列的对调函数指针给DXUT,然后当事件发生DXUT将调用这些函数。
DXUT不需要你一定设置这些回调函数,只有你需要时你才设置。
可以通过DXUTSetCallback***函数来设置一个函数,这个函数有两个参数,第一个指定对调函数的指针,
第二个是void *pUserContext,可以允许回调函数接受一个应用程序的指针,比如一个类指针。
在DXUT里有三种类型的事件:
.Device 事件
.Frame 事件
.Window 事件
渲染设备事件
当应用程序通过Direct3D 9设备来渲染,可能设备会丢失(当按下windows键从全屏模式到窗口模式).或者按下ALT+TAB离开全屏模式,
或者CTRL+ALT+DEL.
当设备丢失后,应用程序将释放所有的Direct3D对象(如果对象被创建是在(D3DPOOL_DEFAULT)内存里,如果这些对象被释放,他们不能
被reset(复原)). 可以调用IDirect3DDevice9::Reset里重新创建所有的对象。
记得以前回了处理这个设备丢失,是通过查看应用程序是否有效来通知设备来 reset.
但在DXUT里,这一过程只需要简单的调用一个回调函数在应用程序里,可以处理各种与设备相关的事件。
例如:changing,created,reset,lost,destroyed.我们要做的就是注册回调函数,实现回调函数。
详细函数信息查看MSDN或者例子代码去找。
这些回调函数不是一定要注册,但是最好注册实现掉。
帧事件:
DXUT也提供了帧事件对调,通过DXUTSetCallbackFrameMove和DXUTSetCallbackD3D9FrameRender注册
前面注册的回调函数是在帧开始的时候调用,后面注册的函数是在帧结束时候调用。
消息事件:
DXUT传递windows消息,键盘事件,鼠标事件通过下面对调函数来设置。然后写这些回调函数来处理这些事件。
DXUTSetCallbackMsgProc 设置window消息处理函数
DXUTSetCallbackKeyboard 设置键盘消息处理函数
DXUTSetCallbackMouse 设置鼠标消息处理回调函数