DirectX SDK 2006學習筆記1——框架

   Copyright © MikeFeng  QQ: 76848502

  友情提醒:所謂的框架是指SDK目錄下/Samples/C++/Common路徑下的DXUT系列函數包裝。學習框架的前提是必須有足夠的Windows API,GUI編程經驗,必須熟悉Windows的消息機制,回調機制,最好有萬行左右的C/C++編程經驗。MFC在這裏沒有任何用處。另外我覺得最好在看程序之前對於D3D的所有概念有點了解,什麼是vertex,texture,matrix,lighting,mesh等等,以及相關的數學概念。這些都可以在網上找到中文翻譯,幫助你快速入門。
 
  DXSDK2006和2003版的比起來更新了不少東西,比如DirectX10,還有Managed
DirectX等等。不過我關心的還是D3D9。除了個別接口的更改之外,DXSDK2006還提供了一套圖形控件的類庫,它的界面還是很漂亮的:)如圖:
 
  學習一個框架還是從它的入口學習比較方便,否則容易迷失在無窮無盡的API和層層包裝之中。DXSDK2006的框架和2003版的DX9.0c框架有很大的不同。首先是2003版的框架中提供了一個CD3DApplication類,這個類對於初始化,清除,以及遊戲窗口的創建,遊戲主循環進行了包裝。這是一個不錯的類,不知道爲什麼在2006版中去掉了。不過不要緊,2006版的框架中提供的一些C包裝函數已經足夠了。在看這些函數之前,我們還是先來看看SDK目錄下/Samples/C++ /Direct3D/Tutorials中有些什麼吧。Tut01_CreateDevice是創建框架,這個程序不用框架,研究一下有助於瞭解D3D的大致工作流程。下面是winmain函數中的一部分。
 
    // Initialize Direct3D
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {
        // Show the window
        ShowWindow( hWnd, SW_SHOWDEFAULT );
        UpdateWindow( hWnd );
 
        // Enter the message loop
        MSGmsg;
        while( GetMessage( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }
  
  在消息循環之前有個初始化設備的函數InitD3D( hWnd ),其代碼如下:
HRESULTInitD3D( HWNDhWnd )
{
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        returnE_FAIL;
 
    D3DPRESENT_PARAMETERSd3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp,
&g_pd3dDevice ) ) )
    {
        returnE_FAIL;
    }
    returnS_OK;
}
 
主要是調用Direct3DCreate9g_pD3D->CreateDevice這兩個函數。查看DXSDK文檔中關於D3DPRESENT_PARAMETERS的定義,大致瞭解一下。
接下來要關心的就是消息循環了,在回調函數MsgProc中處理了兩個消息,一個是WM_DESTROY,裏面調用了Cleanup函數,另一個是WM_PAINT函數,裏面調用了Render函數。Cleanup函數很簡單,就是調用D3D對象及其設備對象的Release函數釋放資源,而Render函數就是D3D中最重要的函數了。
VOIDRender()
{
    if( NULL==g_pd3dDevice)
        return;
 
    // Clear the backbuffer to a blue color
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
   
    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
        // Rendering of scene objects can happen here
   
        // End the scene
        g_pd3dDevice->EndScene();
    }
 
    // Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL);
}
  主要調用的函數有BeginScene, EndScene和Present函數。
    對D3D應用程序有了大概瞭解之後就可以看空框架程序了。這個程序可以在Samples/C++/Direct3D/EmptyProject中找到。
  從WinMain中的調用可以看到,框架首先設定一堆回調函數,很多事情的是在用戶自己寫的回調函數中實現。從DXUTInit開始,程序開始調用框架內的API來完成初始化——創建窗口——創建設備——主消息循環——退出等一系列操作。調查Common目錄下DXUT.cpp文件就可以發現DXUTInit函數幹了以下幾件事情
     設定開始調用這個函數的標誌符
     InitCommonControls
     保存當前的sticky/toggle/filter鍵
     通過事先導入winmm.dll的方法timeBeginPeriod來確保調用Sleep的準確性
     設定一些標誌附,讀取命令行參數
     檢查版本
     獲得D3D對象指針。值得一提的是框架中大部分全局變量是通過類DXUTState的靜態變量state的get/set方法得到的。這些get/set方法是用宏定義的,裏面調用了加鎖和解鎖,因此保證了全局變量設定的線程安全。這些全局性的變量包括D3D對象指針,D3D設備對象指針,BackBufferSurfaceDesc,DeviceCaps,窗口HINSTANCE,窗口句柄HWND,焦點句柄HWNDFocus,全屏設備句柄,窗口設備句柄,窗口客戶端矩形,模式切換時窗口客戶端矩形,模式切換時全屏客戶端矩形,Time,ElapsedTime,FPS數,窗口標題,設備數據DeviceStats,以及是否暫停渲染,時間是否暫停,窗口是否激活等標誌,一些窗口事件等等。這些都可以通過DXUTGETXXX/DXUTSETXXX/DXUTISXXX系列包裝函數獲得。
     通過DXUT_Dynamic_Direct3DCreate9創建D3D對象。很多D3D底層API都是通過動態的方式加載的,這樣有利於效率的提高。
     重設全局時鐘
     設定DXUTInited爲true。很多DXUT系列的函數都喜歡在入口設定一個開始調這個函數的標誌,在出口設定一個這個函數已經被調過的標誌,這樣可以在以後再次調用這個函數的時候瞭解當前什麼工作已經做了,什麼工作沒做需要補做。我想這個主要是用來防止函數重入問題的吧。其他函數中的這一對函數就不再提了
 
  呼~第一個函數大致看完了,接下來是DXUTCreateWindow函數。什麼?要問DXUTSetCursorSettings爲什麼被無視?因爲這個函數不重要。DXUTCreateWindow的工作大致是這樣的
     判斷關於設備的CallBack有沒有設定好
     判斷DXUTInit()有沒有被調用成功(注意不是有沒有調用)。
     獲得焦點句柄,因爲窗口還沒有創建,所以這個句柄應該是NULL
     設定HInstance
     設定窗口類
     註冊窗口類
     設定窗口位置和大小。好長一段代碼,汗
     創建窗口。終於。。。
     設定窗口焦點句柄,全屏設備句柄,窗口設備句柄
  
  接下來的函數是DXUTCreateDevice。這個函數就是用來選擇最優設備並創建的。
     設定參數中的回調函數和上下文,以備後用
     檢查窗口是否被成功創建,否則再調用一次DXUTCreateWindow
     枚舉所有可能的顯示模式。枚舉過程非常複雜,用到了CD3DEnumeration中的一些包裝函數,這些設備信息包括分辨率,顏色位深等等。這裏會用到DXUTCreateDevice傳進來的參數IsDeviceAcceptable
     如果命令行設定過顯示模式,那麼將剛纔得到的信息覆蓋。
     採用某種權重的算法找出最優顯示模式(DXUTFindValidDeviceSettings)
     切換設備。這裏用到了DXUTCreateDevice傳進來的參數ModifyDeviceSettings。切換設備時要考慮很多問題:比如需要暫時忽略WM_SIZE消息;只有在第一次創建設備的時候才用命令行參數;按照需要調用DXUTCreate3DEnvironment和DXUTReset3DEnvironment;分全屏和窗口設備重設;重設完了根據需要處理WM_SIZE消息;顯示窗口,允許WM_SIZE消息等等
 
  最後是DXUTMainLoop。
     檢查是否有重入問題
     設定進入主循環標誌
     檢查設備是否已經被成功創建,沒創建的話用默認參數創建一次
     檢查前面三個函數是否成功調用。汗,又是檢查
     處理窗口消息,注意只有在沒有消息處理的時候才調用DXUTRender3DEnvironment()
     在消息循環退出之後清除加速表。應該是類似SHIFT+X這種鍵盤加速表的清除吧
     更改主循環標誌
 
  還是有必要看一下主消息循環中的DXUTRender3DEnvironment
     檢查設備是否丟失
     在窗口模式下檢查桌面分辨率位深設定,以便重設設備
     嘗試重設設備DXUTReset3DEnvironment
     判斷上次渲染到現在時間(elapsed time)決定是否要進行渲染
     調用用戶的FrameMove函數
     調用用戶的FrameRender函數
     調用Present函數
     更新當前Frame
     根據命令行檢查是否需要關閉應用程序
 
 
  主函數看完之後,剩下的就是一些回調函數了。要正確使用這些回調函數,除了知道它們的作用之外,還需要知道這些函數是何時被調用的。下面是調用順序
  • 程序啓動:InitApp MsgProc IsDeviceAcceptable ModifyDeviceSettings OnCreateDevice OnResetDevice 渲染主循環
  • 渲染主循環:OnFrameMove OnFrameRender
  • 改變設備:ModifyDeviceSettings OnLostDevice 根據需要調用OnDestroyDevice OnResetDevice 渲染主循環
  • 程序退出:OnLostDevice OnDestroyDevice
  下面是各函數的作用:
InitApp
初始化一些圖形控件和GUI的消息處理函數
OnCreateDevice
創建設備時的回調函數,用於創建D3DPOOL_MANAGED資源
OnResetDevice
重設設備時的回調函數,用於創建D3DPOOL_DEFAULT資源
OnFrameMove
動畫實現處,常用於矩陣轉換等操作
OnFrameRender
渲染實現處,常用於渲染場景
OnLostDevice
設備丟失時的回調函數,釋放由OnResetDevice創建的資源
OnDestroyDevice
設備析構時的回調函數,釋放由OnCreateDevice創建的資源
IsDeviceAcceptable
創建設備時用來對所有可用設備進行過濾的函數
ModifyDeviceSettings
更改設備時的回調函數,用於實現更改設備時所需做的其他操作
MsgProc
安排各空件處理消息的順序
OnGUIEvent
程序控件綁定的消息處理回調函數
 
  以上函數均可以更換名字,這裏只是用框架默認的函數名字。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章