混合使用Direct3D立即模式和保留模式(轉)

post0發表於2007-08-12
混合使用Direct3D立即模式和保留模式(轉)[@more@]

  1、介紹

  微軟的Direct3D包括了兩個截然不同的API。高階的保留模式提供了場景和物件管理服務以及構建幾何引擎。低階的立即模式API提供了直接訪問硬體並允許熟練的3D程式設計師執行自己的渲染和場景管理。這種方式在靈活性和執行效能方面都優於保留模式。

  大多數關於Direct3D的資料都把這兩種API分開講解,許多人也以為它們是互不相關的。實際上,我們要指出,在一個程式中,你或許需要同時使用這兩種API。

  在這篇文章中,討論同時使用保留模式和立即模式API的兩種情形:

  

  •環列舉裝置驅動器

  •環在保留模式應用程式中作為使用者可視物件使用執行緩衝

  

  列舉裝置驅動器是一種直截了當的操作,比較簡單。在保留模式應用程式中使用執行緩衝是這篇文章的重點。

  本文假設你已經瞭解了Direct3D。本文不是Direct3D的概述或教程。本文討論了Direct3D聯機文件中沒有講到的問題。這也就是說,你最好先看看Direct3D的文件。

  注意,在一般的詞彙上討論Direct3D時,通常使用Direct3D這個詞代指整個的3D API,既包括保留模式又包括立即模式。但是,根據Drect3D程式碼的約定,使用D3D表示立即模式介面或變數,D3DRM表示保留模式介面或變數。由於這種約定,在討論樣例程式碼或專用介面的時候,Direct3D有時候指的是立即模式而不是整個的3D API集。本文試圖劃清界限,但你也要意識到,樣例程式碼和註釋中也許也會在兩種意義下都用Direct3D這個詞。

  

  2、列舉裝置驅動器

  應用程式使用Direct3D,無論是立即模式還是保留模式,通常需要實時地在使用者計算機上列舉可獲得的(圖形)驅動器。如果影像品質比渲染速度更重要的話,應用程式應該選擇最高的位深(bit depth)和(或)解析度。另一方面,如果需要高速渲染,應用程式會犧牲一些影像質量來換取效能。

  保留模式沒有單獨包含一個列舉驅動器的方法。代替它的是,所有的Direct3D應用程式都使用IDirect3D::EnumDevices方法。DirectX SDK中的程式碼樣例展示瞭如何使用這種方法。可以參看Direct3D幫助檔案中的保留模式教程。使用這種方法列舉驅動器並不困難。我在只這裡稍微說一下,讀者可以參考DirectX聯機文件獲取全部的說明。

  當開發人員需要在保留模式應用程式中呼叫立即模式API的時候,最顯而易見的問題是:“我如何得到一個指向立即模式Direct3D的指標”?這個很簡單:因為Direct3D COM介面準備了一個指向DirectDraw的介面,你可以透過這個DirectDraw介面獲得Direct3D指標(記住,這裡的Direct3D意味著立即模式)。這個可以簡單的分為兩步:

  

  LPDIRECTDRAW lpDD;

  LPDIRECT3D lpD3D;

  HRESULT rval;

  

  DirectDrawCreate (NULL, &lpDD, NULL);

  rval = lpDD->lpVtbl->QueryInterface(lpDD, &IID_IDirect3D, (void**) &lpD3D);

  

  看到了嗎?是不是很簡單?現在我們已經有了一個指向Direct3D介面的指標,接下來,我們就可以輕鬆地呼叫IDirect3D::EnumDevices方法來列舉可獲得的裝置了。從現在開始,需要做的工作同立即模式應用程式一模一樣:定義一個列舉回撥例程,傳遞地址給IDirect3D::EnumDevices方法。回撥函式將會被每一個系統上安裝的驅動器呼叫。從而檢查每個驅動器的特性來確定是否適合應用程式的需要。詳細內容請參考具體的程式碼。

  現在,該看看我們更感興趣的另一個問題:混合使用Direct3D的兩種模式。

  

  3、在保留模式中使用執行緩衝

  有的時候,應用程式可能想要使用執行緩衝(允許執行自己的變換、燈光或光柵等)。但同時還要使用保留模式中提供的更方便的API函式。這可以透過把執行緩衝看作是Direct3D中的可視物件來處理。

  在SDK中,可以找到一個叫做UVIS(User VISual)的例子,這是一個“燃燒的火焰”,它演示了這種技術,我們就以此為例。如果你是那種喜歡自己讀程式碼,自己完成一切的人,那可以不必看下面的內容。如果你喜歡看詳細的解釋,那麼讓我們一起來看看這段程式碼,並討論其中的幾個主要問題。

  

  3.1 編譯準備

  如果你明白怎麼編譯DirectX程式,那麼可以不必向下看。如果你以前從未編譯過任何DirectX程式碼,那麼看看下面的內容。我們使用微軟的Visual C++ 5.0編譯UVIS樣例。假設你的DirectX所在的路徑是C:dxsdksdk,那麼:

  1)建立一個新的的project workspace,在其中增加uvis.cpp、rmmain.cpp、rmerror.c三個檔案

  2)在Tools/Options/Directories中選Include files,加入DirectX標頭檔案所在的路徑,如:c:dxsdksdkinclude

  3)在Tools/Options/Directories中選Library files加入DirectX庫檔案所在的路徑,如:c:dxsdksdklib

  4)在Project/Settings/Link中的Object/Library Modules加入連結時需要的庫:d3drm.lib、ddraw.lib、winmm.lib

  

  3.2 Direct3D保留模式樣例的組織

  為了簡化,在Direct3D SDK中所有保留模式樣例共享一些通用程式碼。這使你能夠把精力集中在核心程式碼上,而不必在通用的程式碼上浪費時間。保留模式通用程式碼包括兩個檔案:RMMAIN.CPP和RMERROR.C。

  通用部分包括建立和管理標準Windows應用程式以及執行基本保留模式初始化和處理的程式碼。在下面的內容中,我們來看看UVIS的主要部分。我們不會詳細的討論每一個函式呼叫。我的目的不是解釋保留模式或立即模式的用法,而是展示它們如何在一個應用程式中同時被使用。當然,我也希望這篇文章能讓你迅速的理解如何把這些程式碼組織在一起。

  

  3.3 RMMAIN.CPP做了些什麼

  在RMMAIN.CPP中的程式碼形成了Direct3D保留模式應用程式的一般框架。最重要的部分包含在WinMain()函式中,在這個函式里,由兩個主要的部分:應用程式安裝和初始化階段以及訊息迴圈,實際的渲染就發生在這個階段。

  應用程式安裝和初始化。在InitApp()函式中,壓縮了大部分的初始化程式碼。這個函式是最普通的。它先安裝通常的視窗類,然後是初始化一些全域性遍量。緊接著是呼叫OverrideDefaults(),這個函式透過填充如下結構定義了一些它自己的設定:

  

  typedef struct Defaultstag {

  BOOL bNoTextures;

  BOOL bResizingDisabled;

  BOOL bConstRenderQuality;

  char Name[50];

  } Defaults;

  

  下一個視窗以Windwos通常的風格建立,樣例程式碼透過如下幾步設定保留模式應用程式:列舉裝置,建立主D3DRM物件,建立主要的場景和攝像機,設定渲染品質等等。在視窗能被看見之前,InitApp()呼叫UVIS.CPP 中的BuildScene()函式。這是最激動人心的地方。在這裡我們要討論一下樣例程式碼究竟是如何工作的:既然應用程式被初始化了,下一步就是迴圈。

  訊息/渲染迴圈。一旦應用程式被初始化,WinMain()設定標準Windows訊息迴圈。在這個迴圈中就包括了對RenderLoop()的呼叫。RenderLoop()在Direct3D保留模式例子中執行把物件渲染到螢幕上去的大部分工作。 RenderLoop()對三個不同的保留模式介面進行一系列四個呼叫。首先,它呼叫IDirect3DRMFrame::Move()對所有框架應用旋轉和速度。然後呼叫IDirect3DRMViewport::Clear()清除當前的視口和設定背景顏色。下一步是呼叫IDirect3DRMViewport::Render()把當前場景渲染到當前視口上。最後IDirect3DRMDevice::Update()複製渲染的影像來顯示。

  透過IDirect3DRMViewport::Render()呼叫,所有的工作都實際上完成了。在這裡,系統呼叫了場景中的每一個物件,通知物件渲染它自己。又及,這也是我們將要在下個部分看到的,我們在暗中透過立即模式把物件渲染到保留模式應用程式中。

  

  3.4 UVIS.CPP做了些什麼

  我們在這部分裡講到的雖然很簡單,但卻是保留模式應用程式的標準框架。現在我們來看看UVIS演示的技術(這也是本文的焦點):在保留模式中使用使用者可視物件的能力。

  正如我們在RMMAIN.CPP看到的,InitApp()呼叫在UVIS.CPP中定義的BuildScene()函式。我們不是在保留模式應用程式中僅僅增加一堆保留模式物件,代替它的是在物件上增加了一個使用者可視物件,代表執行緩衝和它的建立以及渲染例程。使用者可視物件是一個簡單的使用者定義的可視物件,與其它的預定義的可視物件一樣增加到場景中,而由開發人員提供建立和渲染例程。

  設定使用者可視物件。BuildScene()從為場景建立一些燈光開始。然後呼叫CreateFire(),這個函式實際建立了在使用者可視物件中用到的立即模式物件。讓我們透過實際程式碼看看它是怎麼做的。

  首先,看看在UVIS.CPP中定義的檔案結構。

  

  typedef struct _Fire {

  Flame flames[MAX_FLAMES];

  LPDIRECT3DRMDEVICE dev;

  LPDIRECT3DEXECUTEBUFFER eb;

  LPDIRECT3DMATERIAL mat;

  } Fire;

  

  CreateFire()建立了一個檔案結構,包含我們要建立的使用者可視物件(帶有幾個火焰的火)的資訊。檔案結構包含每一個火焰的資料(包括火焰的位置、速度、生命期等)。指向保留模式裝置,執行緩衝和材質。結構初始化為空。

  

  Fire* fire;

  fire = (Fire*)malloc(sizeof(Fire));

  if (!fire)

  goto ret_with_error;

  memset(fire, 0, sizeof(Fire));

  

  IDirect3DRM::CreateUserVisual()函式建立一個使用者可視物件,並傳遞會在uvis變數中這個物件的地址。同這個物件關聯的是應用程式定義的資料(在本例中是Fire結構)和回撥(在本例中是FireCallback())。在系統想要應用程式渲染使用者可視物件的時候這些被呼叫。

  

  LPDIRECT3DRMUSERVISUAL uvis = NULL;

  if (FAILED(lpD3DRM->CreateUserVisual(FireCallback, (void*) fire, &uvis)))

  goto ret_with_error;

  The DestroyFire() callback will be called w

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-952244/,如需轉載,請註明出處,否則將追究法律責任。

相關文章