遊戲開發學堂:Direct3D入門之我見(轉)

post0發表於2007-08-12
遊戲開發學堂:Direct3D入門之我見(轉)[@more@]

  Direct3D 是DirectX的成員之一.顧名思義,他是DirectX中負責實現3D圖形繪製的部分.現在讓我們來討論一下如何編制一個簡單的3D程式的問題.

  

  D3D是一個強大的三維圖形繪製使用介面.它提供的高階保留模式(Retain mode) 介面功能強大又方便易用,十分適合初學者使用,所以我們使用這個介面來構造我們的程式.

  

  我們的目的是寫一個全屏模式的程式.這就需要使用Direct Draw.更關鍵的是,D3D Retain mode(以下簡稱RM)是以DirectDraw為基礎才能實現的.如果你對DirectDraw瞭解不多,也沒關係,因為我們將要使到的僅是DirectDraw中極少的一部分.

  

  首先我們透過DirectDraw函式建立DirectDraw物件,函式定義如下:

  

  HRESULT DirectDrawCreate(

  GUID FAR lpGUID,

  LPDIRECTDRAW FAR *lplpDD,

  IUnknown FAR* pUnkOutter

  );

  

  第一個引數是全域性唯一識別符號,代表著要使用的驅動程式.我們在這裡簡單的使用NULL填充以表示使用活動的顯示驅動程式.第二個引數是個指標,如果呼叫成功則會把建立的DDraw物件地址付給它.

  第三個引數填NULL就行了.如果呼叫成功則返回DD_OK.

  

  然後我們就可以用DirectDraw的SetCooperativeLevel函式設定合作級別為獨佔全屏.函式的一般形式如下:

  

  HRESULT SetCooperativeLevel(

  HWND hwnd,

  DWORD dword

  );

  

  第一個引數是應用程式的視窗控制程式碼.第二個引數用 DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN填充表示使用獨佔全屏模式.這種模式效率較高,是一般遊戲的通用模式.程式碼形式如下:

  

  LPDIRECTDRAW lpDD;

  HRESULT hr;

  ......//建立DDraw物件,錯誤處理,等

  

  hr=lpDD->SetCooperativeLevel(NULL,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN);

  if(hr!=DD_OK)

  //錯誤處理

  

  此後我們就可以設定顯示模式了.DirectDraw的SetDisplayMode函式用來設定顯示模式.

  

  //假設已有LPDIRECTDRAW物件的指標lpDD

  

  hr=lpDD->SetDisplayMode(640,480,16,0,0);

  if(hr!=DD_OK)

  //錯誤處理

  

  為了儘可能簡化,我們沒有使用列舉函式來得到可用的顯示模式而是直接使用了一般顯示裝置都支援的640*480解析度,16位色.

  

  下一步是建立影像輸出的目標--主表面.透過DirectDraw的CreateSurface函式來建立表面.首先填充表面的特徵到一個DDSURFACEDESC結構.

  

  LPDIRECTDRAWSURFACE lpDDSPrimary=NULL; //主表面

  DDSURFACEDESC ddsd;

  ......

  ZeroMemory(&ddsd,sizeof(ddsd));

  ddsd.dwSize=Sizeof(ddsd);

  ddsd.dwFlags=DDSD_CAPS|DDSD_BACKBUFFERCOUNT;

  ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE| //主表面

  DDSCAPS_FLIP| //可實現翻轉動畫

  DDSCAPS_COMPLEX|

  DDSCAPS_VIDEOMEMORY|

  DDSCAPS_3DDEVICE; //可以作為D3D 裝置

  ddsd.dwBackBufferCount=1; //後備緩衝表面數目為1

  hr=lpDD->CreateSurface(&ddsd,&lpDDSPrimary,NULL);

  if(hr!=DD_OK)

  //錯誤處理

  

  如果建立成功,我們就有了一個有後緩衝,可實現翻轉的,可作為D3D裝置的主表面(關於D3D裝置,後面還要講到)然後我們用GetAttachedSurface函式獲得指向後備緩衝的指標:

  

  DDSCAPS ddscaps;

  LPDIRECTDRAWSURFACE lpDDSBackbf;

  ......

  ddscaps.dwCaps=DDSCAPS_BACKBUFFER|DDSCAPS_3DDEVICE ;

  hr=lpDDSPrimary->GetAttachedSurface(&ddscaps,&lpDDSBackbf);

  if(hr!=DD_OK)

  //錯誤處理

  

  一切準備工作就緒,我們就可以開始使用D3D RM了.

  

  RM的最大好處就在於其形象性.不必再在十分抽象的層面上去理解"物件"這一概念.我們完全可以把物件理解為諸如光源,3D物體和攝像機之類的實物.

  

  首先透過Direct3DRMCreate建立一個D3D保留模式物件.它是一切RM物件的基礎,只有透過它才能建立其他RM物件.

  

  LPDIRECT3DRM lpD3Drm;

  ......

  hr=Direct3DRMCreate(&lpD3Drm);

  if(hr!=DD_OK)

  //錯誤處理

  

  然後我們用QueryInterface方法查詢一個更新版本的介面並使用它:

  

  hr =lpD3Drm->QueryInterface(IID_IDirect3DRM3,(LPVOID *)&lpD3DrmNew);

  if(FAILED(hr))

  //錯誤處理

  

  這時舊的介面已經無用,釋放它:

  lpD3Drm->Release();

  

  前面我們已經建立了一個DirectDraw表面(Surface)物件,現在我們用它來建立D3DRM裝置(Device)物件.裝置物件代表了最終影像渲染到的裝置和所用的顯示驅動程式.在這裡使用的是預設裝置即顯示器.函式如下:

  

  HRESULT CreateDeviceFromSurface(

  LPGUID lpGUID,

  LPDIRECTDRAW lpDD,

  LPDIRECTDRAWSURFACE lpDDSBack,

  LPDIRECT3DRMDEVICE3 * lplpD3DRMDevice,

  );

  

  第一個引數是指向GUID結構變數的指標.GUID是"全域性唯一識別符號",是Windows系統對可用裝置的唯一標識.在這裡指的是渲染所用的方式(如軟加速,硬加速)為了儘可能簡化程式可以直接用NULL表示用預設的方式.建立過程程式碼如下:

  

  LPDIRECT3DRMDEVICE3 lpDevice;

  hr=lpD3DrmNew->CreateDeviceFromSurface(NULL,(LPDIRECTDRAW)lpDD,

  lpDDSBackbf,

  0,&lpDevice);

  if(FAILED(ddrval))

  //錯誤處理

  

  現在我們終於可以建立真正意義上的3D物件了.我們有必要先弄清"Frame"(框架)這一概念."Frame"就像一個玻璃盒子,裡面可以裝任何3D物體,如飛機,坦克,食人魔.Frame可以被設定方向和位置,這時它所裝的3D物體也會與它保持同樣的方向,位置.Frame是D3DRM的靈魂.

  

  一個場景往往包含一個由Frame構成的層次,即由根Frame及其子Frame(子Frame還可以有子Frame)構成的樹型結構.父Frame和子Frame的關係,就像大臂和前臂的關係一樣,大臂在自己運動的同時,可以決定前臂的運動狀態,反之不可.根Frame是沒有父Frame的.

  

  用CreateFrame函式建立Frame:

  

  HRESULT CreateFrame(

  LPDIRECT3DRMFRAME lpParentFrame,

  LPDIRECT3DRMFRAME * lpFrame,

  );

  

  第一個引數是作為父Frame的Frame的地址.第二個引數是要建立的 Frame.建立過程如下:

  

  LPDIRECT3DRMFRAME lpRoot;

  LPDIRECT3DRMFRAME lpParent;

  LPDIRECT3DRMFRAME lpChild;

  ......

  lpD3DrmNew->CreateFrame(NULL,&lpRoot); //根Frame

  lpD3DrmNew->CreateFrame(lpRoot,&lpParent); //父Frame(在這裡又是Root的子Frame.)

  lpD3DrmNew->CreateFrame(lpParent,&lpChild); //Parent的子Frame.

  //省略了錯誤處理

  

  建立完Frame後,就可以向裡面加入3D模型了.最簡單的方法是用Frame物件的load函式:

  

  HRESULT Load(

  LPVOID lpvObjSource,

  LPVOID lpvObjId;

  D3DRMLOADOPTIONS flags,

  D3DRMLOADTEXTUER3CALLBACK d3drmloadtexturecallback,

  LPVOID lpArgLTP

  );

  

  使用時一般形式如下:

  

  lpRoot->Load("backgrd.x",NULL,D3DRMLOAD_FROMFILE,NULL,NULL);

  lpParent->Load("parent.x",NULL,D3DRMLOAD_FROMFILE,NULL,NULL);

  lpChild->Load(......

  ......

  //省略出錯處理

  

  第一個引數是已有的3D模型檔案(*.xfile),可以先用建模軟體(如3DSMAX)建立模型並匯出為*.3DS檔案,再用DirectX開發工具包(SDK)提供的CONV3DS工具將其轉化為*.X檔案.例如我們有一名為backgrd.3ds的檔案,要把它轉化為Frame物件能Load的.x檔案,需要按如下方式執行:

  

  conv3ds -T -X backgrd.3ds

  

  即可得到名為backgrd.x的檔案.關於conv3ds 的其他引數及用法,請參閱DirectX SDK 幫助文件.

  

  載入模型到frame還有其他方法如使用網格生成器物件,在這裡不再贅述.

  

  我們把3D模型載入到frame以後,還需要在場景中引入燈光和攝像機這兩個必不可少的東西。首先為攝像機建立一個frame,建立方法和普通frame一樣:

  

  LPDIRECT3DRMFRAME lpCameraFrame;

  ……

  lpD3DrmNew->CreateFrame(lpRoot,&lpCameraFrame);

  

  然後就可以建立視口(攝像機)了:

  HRESULT CreateViewport(

  LPDIRECT3DRMDEVICE lpDev,

  LPDIRECT3DRMFRAME lpCamera,

  DWORD dwXPos,

  DWORD dwYPos,

  DWORD dwWidth,

  DWORD dwHeight,

  LPDIRECT3DRMVIEWPORT * lpViewport

  );

  

  第一個引數是我們前面創造的裝置,第二個引數是攝影機frame,下面四個引數是視口位置和尺寸。呼叫時程式形式如下:

  

  LPDIRECT3DRMVIEWPORT lpViewport

  int width,height;

  ……

  width= lpDevice->GetWidth();

  height= lpDevice->GetHeight();

  hr=lpD3DrmNew->CreateViewport(lpDevice,lpCameraFrame,0,0,width,height,

  &lpViewport);

  if(hr!=DD_OK)

  ……

  

  在創造Viewport的同時,也就把它裝載到了frame中。

  然後為光源建立frame:

  

  LPDIRECT3DRMFRAME lpLightFrame;

  ……

  lpD3DrmNew->CreateFrame(lpRoot,&lpLightFrame);

  ……

  

  建立燈光的函式如下所示:

  HRESULT CreateLightRGB(D3DRMLIGHTTYPE ltLightType,

  D3DVALUE

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

相關文章