转:DirectX8.0初体验, 有点老,但写的挺好
有時(shí)候從一張白紙開始的感覺(jué)還是不錯(cuò)的。微軟的上一個(gè)版本(DirectX7.0)是一個(gè)新的開始,畫圖元
(DrawPrimitive)開始形成了一種風(fēng)格。
DirectX8(DX8)真正體現(xiàn)了其成熟之處。許多操作得到了改進(jìn)和變得簡(jiǎn)單,其高級(jí)功能更為易用。一個(gè)簡(jiǎn)單的例
子,一個(gè)500行程序行的OpenGL程序,如果用DirectX8.0寫的話,大約在1000行左右。如今,DirectX8和OpenGL
看起來(lái)已經(jīng)非常相似了。
DirectX8的變化實(shí)在是太大了,我不想花太多的時(shí)間去講述都發(fā)生了什么改變。取而代之的是,我將討論更多
的是現(xiàn)在的DirectX8.0 API更象什么,以及你如何立即取得更多的優(yōu)勢(shì)。
DX8由6組API組成:DirectX圖形(包括Direct3D和D3DX函數(shù)庫(kù)),DirectX Audio(包括Direct Sound和Direct
Music),DirectInput, DirectPlay, DirectSetup, 和 DirectShow。DX8是非常龐大的,因此,我不想在這里
討論所有的DX8 API,本文會(huì)指導(dǎo)你使用DX8進(jìn)行圖形和視頻編程。
我不知到是否有更新版本的DirectX Media會(huì)出現(xiàn)。DirectShow原來(lái)是DirectX Media的一部分,現(xiàn)在它已經(jīng)是
DirectX8.0運(yùn)行庫(kù)的一個(gè)基本部分了。對(duì)于開發(fā)者來(lái)講這是有利的,因?yàn)椴挥迷俜謩e安裝兩個(gè)運(yùn)行庫(kù)。Direct
3D保留模式是Direct Media的一個(gè)組成部分,但D3DX函數(shù)庫(kù)更有取代它的可能。我想DirectX將會(huì)不斷地得來(lái)改
進(jìn),而Direct Media不再有改變,但舊的DirectX Media6.1仍然是可用的。
DirectX圖形
或許DirectX8.0中最令人耀眼改變就是DirectDraw了,DirectDraw完全地被Direct3D所取代了。
Direct3D作了重大的改動(dòng),引入了許多新的特性。你不再須要列舉任何的東西(設(shè)備)。Direct3D僅由12個(gè)接口
組成,它的遺傳圖表是非常單的:
(圖)
其中最COOL的一點(diǎn)特性就是加入了一種陰影語(yǔ)言(shader language)。和Renderman或Quake 3的陰影語(yǔ)言相比,
M$的的陰影語(yǔ)言更類似于匯編語(yǔ)言(assembly language)。然而它們的概念卻是一樣的。
D3DX是DirectX的一個(gè)高級(jí)函數(shù)庫(kù)。D3DX函數(shù)庫(kù)是非常靈活的,其包含的API可用于創(chuàng)建從精靈(sprites)、文字
到粘圖等所用的東西。使用D3DX將使你的取得跳躍性的進(jìn)展。
DX8的矩陣操作是非常清析的(由其是在D3DX當(dāng)中),并且工作起來(lái)更象OpenGL了。以下是其它一些與OpenGL相似
的地方:
Direct3D ?OpenGL
BeginScene?glBegin?
EndScene?glEnd
DrawPrimitive?glDrawElements
SetRenderState?glEnable
SetTexture?glBindTexture
Clear??glClear
許多的類似之處已經(jīng)在早其版本中的DirectX中出現(xiàn)了,但新的DX8中明顯地體現(xiàn)到DX8與OpenGL有許多相似之處
。
2D編程并沒(méi)有完全被取代,DX8的D3DX庫(kù)中有一個(gè)精靈(scripte)接口。然而首選的2D編程方式應(yīng)該是使用簡(jiǎn)單
的粘圖。色彩鍵(chroma keying)被取消了,做透明效果的唯一方法是使用alpha混合(alpha blending)。
或許你會(huì)說(shuō)我會(huì)很快成為這套API的狂熱者。或許你只習(xí)慣于OpenGL。但DX8作了許多改進(jìn),使用DX8開發(fā)你的游
戲已經(jīng)不再成為問(wèn)題了,我們將寫一些DX8的代碼來(lái)說(shuō)明這一點(diǎn)。
DirectX圖形API是非常簡(jiǎn)單而且功能強(qiáng)大的。當(dāng)你使用DX8一段時(shí)日子后你會(huì)希望其它的DX8 API也象DirectX8
圖形API那樣簡(jiǎn)單易用。
DirectShow
DirectShow是M$的視頻API,你可以在這里找到錄象機(jī)、數(shù)碼攝象機(jī)和DVD播放機(jī)等。游戲開發(fā)者可以很容易的
把視頻電影加進(jìn)他門的游戲中。
有一些新的特性加入了DirectShow,但與游戲開發(fā)關(guān)系不大。例如歐洲的PAL制式,M$的ASF等。
播放Video是游戲開發(fā)者使用DirectShow的主要原因,一會(huì)兒我們將會(huì)寫一些這樣的代碼。
我們的程序主體
我會(huì)講解幾個(gè)演示程序,為了簡(jiǎn)單起見,所有的演示程序都是基于同一程序主體的。
程序主體大約有90行,它是一個(gè)簡(jiǎn)單的Win32程序。它的作用是建立一個(gè)Windows窗口并調(diào)用我定義的DirectX函
數(shù)。這些函數(shù)是InitDirect3D,ShutdownDirect3D,和DrawScene。
所有的Demo都使用同樣的ShutdownDirect3D函數(shù)和變量。
#define HELPER_RELEASE(x) { if(x) { (x)->Release(); (x) = NULL; }}
IDirect3D8 * pID3D?????????????????? = NULL;
IDirect3DDevice8 * pID3DDevice?????? = NULL;
IDirect3DVertexBuffer8 * pStreamData = NULL;
IDirect3DIndexBuffer8 * pIndexBuffer = NULL;
IDirect3DTexture8 * pTexture???????? = NULL;
void ShutdownDirect3D()
{
? HELPER_RELEASE(pTexture);
? HELPER_RELEASE(pIndexBuffer);
? HELPER_RELEASE(pStreamData);
? HELPER_RELEASE(pID3DDevice);
? HELPER_RELEASE(pID3D);
}
我首先定義我所用到的接口。注意:并不是所有的demo都會(huì)用到所有的接口的。只是為了程序主體的統(tǒng)一和簡(jiǎn)
單化而已。
ShutdownDirect3D釋放所有的接口。將來(lái)你可能要加入額外的代碼來(lái)關(guān)閉Direct3D接口,但現(xiàn)在已經(jīng)夠了。
現(xiàn)在讓我們開始我們的初始化代碼吧,請(qǐng)定位到InitDirect3D函數(shù)。IDirect3D是我們首先要用到的接口,你可
以這樣寫:
IDirect3D8 * pID3D = Direct3Dcreate8(D3D_SDK_VERSION);
在你使用pID3D以前,請(qǐng)檢查pID3D是否為非空。
你下一步通常是創(chuàng)建D3D設(shè)備,但在創(chuàng)建D3D設(shè)備之前你要調(diào)用GetAdapterDisplayMode方法取得必須的信息:
D3DDISPLAYMODE d3ddm;
pID3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
接下來(lái)是取得當(dāng)前顯示模式參數(shù)。下面的參數(shù)是Surface格式。你可以用這些參數(shù)來(lái)創(chuàng)建一個(gè)
D3DPRESENT_PARAMETERS結(jié)構(gòu):
D3DPRESENT_PARAMETERS present;
ZeroMemory(&present, sizeof(present));
present.SwapEffect????????????? = D3DSWAPEFFECT_COPY;
present.Windowed??????????????? = TRUE;
present.BackBufferFormat??????? = d3ddm.Format;
D3DPRESENT_PARAMETERS描述了顯示器Surface的信息,交換機(jī)制的類型,應(yīng)用程序是窗口的還是全屏模式等信
息。
在本例中,Surface是以拷貝方法代替頁(yè)面翻轉(zhuǎn)的,因?yàn)樗且粋€(gè)窗口模式的應(yīng)用程序。把后臺(tái)表面設(shè)置成與當(dāng)
前顯示模式相匹配的格式,一個(gè)準(zhǔn)備顯示的Surface可以Draw在后臺(tái)表面上。
現(xiàn)在你可以創(chuàng)建一個(gè)IDirect3DDevice8接口了:
pID3D->CreateDevice(D3DADAPTER_DEFAULT,
??????????????????? D3DDEVTYPE_HAL,
??????????????????? hwnd,
??????????????????? D3DCREATE_SOFTWARE_VERTEXPROCESSING,
??????????????????? &present,
??????????????????? &pID3DDevice);
這個(gè)函數(shù)有六個(gè)參數(shù),幸運(yùn)的是沒(méi)有一個(gè)是很復(fù)雜的。D3DADAPTER_DEFAULT告訴Direct3D使用主顯示器,只有
當(dāng)你使用多顯示器時(shí)才是必須的。你可以使用一個(gè)數(shù)值來(lái)指定另外一個(gè)顯示器。調(diào)用IDirect3D的
GetAdapterCount將返回系統(tǒng)的適配器數(shù)目。
第二個(gè)參數(shù),D3DDEVTYPE_HAL,告訴Direct3D使用硬件加速。其它選項(xiàng)包括D3DDEVTYPE_REF 和 D3DDEVTYPE_SW
,通常你都會(huì)希望使用硬件加速的,但有時(shí)侯你可能會(huì)使用軟件加速進(jìn)行測(cè)試。
指定窗口取得焦點(diǎn)。如果是全屏應(yīng)用程序,你需要一個(gè)最頂層窗口。
D3DCREATE_SOFTWARE_VERTEXPROCESSING指定頂點(diǎn)處理類型。你也可以使用硬件加速或是聯(lián)合類型,我不使用硬
件加速為的是廣泛的兼容性。如果你想支持T&L,則你必須使用硬件加速。
最后兩個(gè)參數(shù)很簡(jiǎn)單,一個(gè)是你以前建立的,而pID3Ddevice是你現(xiàn)在要?jiǎng)?chuàng)建的IDirect3DDevice8接口。如果方
法返回D3DERR_NOTAVAILABLE,則你寫的參數(shù)是正確的,但你的設(shè)備不支持你指定的參數(shù)。
最完美的是這個(gè)方法自動(dòng)為你創(chuàng)建后臺(tái)緩沖(back buffers)和深度緩沖(depth buffers)。剪裁(Clipping
)作為后臺(tái)表面(backface culling)被自動(dòng)激活。燈光也被自動(dòng)激活了,直到你定義頂點(diǎn)顏色之前,你可以
禁止使用燈光:
pID3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
InitDirect3D函數(shù)已經(jīng)完成,讓我們來(lái)看一看完整的InitDirect3D函數(shù)吧:
HRESULT InitDirect3D(HWND hwnd)
{
? pID3D = Direct3DCreate8(D3D_SDK_VERSION);
? HRESULT hr;
? do
? {
??? // we need the display mode so we can get
??? // the properties of our back buffer
??? D3DDISPLAYMODE d3ddm;
??? hr = pID3D->GetAdapterDisplayMode(
?????????????????????? D3DADAPTER_DEFAULT,
?????????????????????? &d3ddm);
??? if(FAILED(hr))
????? break;
??? D3DPRESENT_PARAMETERS present;
??? ZeroMemory(&present, sizeof(present));
??? present.SwapEffect?????? = D3DSWAPEFFECT_COPY;
??? present.Windowed???????? = TRUE;
??? present.BackBufferFormat = d3ddm.Format;
??? hr = pID3D->CreateDevice(D3DADAPTER_DEFAULT,
???????????????????????????? D3DDEVTYPE_HAL,
???????????????????????????? hwnd,
???????????????????????????? D3DCREATE_SOFTWARE_VERTEXPROCESSING,
???????????????????????????? &present,
???????????????????????????? &pID3DDevice);
??? if(FAILED(hr))
????? break;
??? hr = pID3DDevice->SetRenderState(D3DRS_LIGHTING,
???????????????????????????????????? FALSE);
? } while(0);
? return hr;
}
現(xiàn)在讓我們把注意力集中到DrawScene函數(shù)吧,在我們的第一個(gè)練習(xí)中我只想放一些簡(jiǎn)單的東西在場(chǎng)景中。當(dāng)你
達(dá)到這一點(diǎn)后,在這上面加些東西是很簡(jiǎn)單的事。
下面是DrawScene函數(shù):
HRESULT DrawScene()
{
? HRESULT hr;
? do
? {
??? // clear back buffer
??? hr = pID3DDevice->Clear(0,
??????????????????????????? NULL,
??????????????????????????? D3DCLEAR_TARGET,
??????????????????????????? D3DCOLOR_RGBA(0,63,0,0),
??????????????????????????? 0,
??????????????????????????? 0);
??? if(FAILED(hr))
????? break;
??? // start drawing
??? hr = pID3DDevice->BeginScene();
??? if(FAILED(hr))
????? break;
??? // Put all drawing code here
??? hr = pID3DDevice->EndScene();
??? if(FAILED(hr))
????? break;
??? // flip back buffer to front
??? hr = pID3DDevice->Present(NULL, NULL, NULL, NULL);
? } while(0);
? return hr;
}
這段代碼是很簡(jiǎn)潔的。Clear會(huì)填充你指定的緩沖區(qū)。你可以填充Z緩沖區(qū)、后臺(tái)緩沖區(qū)或摸板緩沖區(qū)(stencil
buffer)。在這個(gè)例子中你將用綠色填充后臺(tái)緩沖區(qū)。所以,我們?cè)O(shè)定D3DCLEAR_TARGET標(biāo)志和綠色。
在本例中BeginScene和EndScene并沒(méi)有做什么,但在以后的例子中我們會(huì)用到它的。這兩個(gè)函數(shù)是畫圖元時(shí)的
例行公事代碼,
這個(gè)函數(shù)不斷的翻轉(zhuǎn)后臺(tái)表面。我們可以不斷的在后臺(tái)表面畫一些東西,然后把后臺(tái)表面翻轉(zhuǎn)到前臺(tái)表面。
如果你現(xiàn)在就運(yùn)行程序,你將得到一個(gè)綠色背景的窗口。如果一切都正常,你就可以編寫代碼去畫一個(gè)三角形
了,要知道畫圖元乃是游戲編程的核心。
畫三角形(d3d2.cpp)
三角形有幾個(gè)有趣的特性,是它們?cè)谌S編程中吸引人的地方。它們總是平面,三角形的組合可以成為任何的
幾何體。在后面的例子中我們會(huì)用三角形去建立一個(gè)立方體。通常我會(huì)在我的第一個(gè)三維引擎中做一個(gè)立方體
旋轉(zhuǎn)的例子。如果它適合于一個(gè)程序員,那么它也適合于另外一個(gè)程序員。
在它的最簡(jiǎn)單形式當(dāng)中,是一個(gè)由三個(gè)頂點(diǎn)組成的三角形。程序員是怎樣定義這些頂點(diǎn)的呢。一個(gè)二維的三角
形你可以用x和y軸坐標(biāo)系來(lái)定義每一個(gè)點(diǎn)。一個(gè)完善的三維程序可能會(huì)定義頂點(diǎn)的坐標(biāo)系,轉(zhuǎn)換坐標(biāo)系,顏色
,幾個(gè)紋理坐標(biāo)系和其它一些信息。
從嚴(yán)格的語(yǔ)義學(xué)來(lái)講,如何運(yùn)用這些信息在OpenGL和Direct3D之間只有些微的區(qū)別。畫一些不連續(xù)的三角形時(shí)
,你可以分別的定義它們。當(dāng)你畫一個(gè)模型的時(shí)候許多的頂點(diǎn)在三角形之間是共享的,所以,為每一個(gè)三角形
保存三個(gè)頂點(diǎn)是低效率的。在OpenGL和Direct3D中你都可以在一個(gè)大的陣列中為一個(gè)模型指定所有的頂點(diǎn)。三
角形的個(gè)數(shù)被定義成三的整數(shù)倍。你可以把這個(gè)陣列傳給一個(gè)函數(shù),例如,DrawIndexedPrimitive,使用它你
可以立即畫出一個(gè)三維模型。
定義你的頂點(diǎn)格式,Direct3D引入了一種可變形頂點(diǎn)格式(flexible vertex format)(FVF)的概念。在FVF
中,你定義一個(gè)結(jié)構(gòu)其中包括所需要的頂點(diǎn)組成部分。這個(gè)結(jié)構(gòu)會(huì)隨著你的程序而改變,但在這里你將初步把
它定義成這個(gè)樣子:
struct MYVERTEX
{
? FLOAT x, y, z; // The transformed position
? FLOAT rhw;???? // 1.0 (reciprocal of homogeneous w)
? DWORD color;?? // The vertex color
};
示例的開始定義了一個(gè)頂點(diǎn)結(jié)構(gòu),頂點(diǎn)的名稱,和每一個(gè)三角形的頂點(diǎn)。在你的InitDirect3D函數(shù)中,你必須
創(chuàng)建一個(gè)頂點(diǎn)緩沖區(qū):
int num_elems = sizeof(vertices) / sizeof(vertices[0]);
pID3DDevice->CreateVertexBuffer(sizeof(MYVERTEX) *
??????????????????????????????? num_elems,
??????????????????????????????? D3DUSAGE_WRITEONLY,
??????????????????????????????? D3DFVF_XYZRHW|D3DFVF_DIFFUSE,
??????????????????????????????? D3DPOOL_DEFAULT,
??????????????????????????????? &pStreamData);
函數(shù)的第一個(gè)參數(shù)是頂點(diǎn)結(jié)構(gòu)的字節(jié)大小。在應(yīng)用程序還不能讀取頂點(diǎn)之前,傳一個(gè)D3DUSAGE_WRITEONLY標(biāo)記
給它。這里可以有不同的標(biāo)記來(lái)指定如何處理你的頂點(diǎn),但現(xiàn)在你可以確信Direct3D已經(jīng)能正確的工作了。
下一步,指定我們用的是什么FVF格式。當(dāng)你還沒(méi)有使用坐標(biāo)系預(yù)轉(zhuǎn)換之前,指定為D3DFVF_XYZRHW標(biāo)記。以后
你使用自己的矩陣坐標(biāo)系轉(zhuǎn)換時(shí),把它改成D3DFVF_XYZ。D3DFVF_DIFFUSE告訴Direct3D,我們將為每一個(gè)頂點(diǎn)
指定顏色。D3DPOOL_DEFAULT指定內(nèi)存的管理模式。
最后一個(gè)參數(shù)是頂點(diǎn)緩沖區(qū)的指針,在例子1中你已經(jīng)定義了它,但并沒(méi)有用上。
如果你不向頂點(diǎn)緩沖區(qū)填入有用數(shù)據(jù)的話,頂點(diǎn)緩沖區(qū)是沒(méi)有用的:
MYVERTEX *v;
pStreamData->Lock(0, 0, (BYTE**)&v, 0);
for(int ii = 0; ii < num_elems; ii++)
{
? v[ii].x???? = vertices[ii].x;
? v[ii].y???? = vertices[ii].y;
? v[ii].z???? = vertices[ii].z;
? v[ii].rhw?? = vertices[ii].rhw;
? v[ii].color = vertices[ii].color;
}
pStreamData->Unlock();
這是不難理解的,Lock返回一個(gè)你想寫入頂點(diǎn)數(shù)據(jù)的指針。下一步你從你的頂點(diǎn)陣列中拷貝數(shù)據(jù)。然后,反還
這個(gè)指針。
這一對(duì)的調(diào)用可以告訴Direct3D你的FVF格式,并設(shè)定頂點(diǎn)陣列為當(dāng)前的活動(dòng)頂點(diǎn)陣列。(你可以有多個(gè)頂點(diǎn)陣
列)。
pID3DDevice->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
pID3DDevice->SetStreamSource(0, pStreamData, sizeof(MYVERTEX));
SetVertexShader告訴Direct3D使用與CreateVertexBuffer同樣的格式。
SetStreamSource告訴Direct3D使用pStreamData作為當(dāng)前頂點(diǎn)陣列,并取得所有元素的大小。
你現(xiàn)在可以加入畫三角形的代碼了。在BeginScene和EndScene之間加入如下代碼:
int num_elems = sizeof(vertices) / sizeof(vertices[0]);
pID3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,
?????????????????????????? 0,
?????????????????????????? num_elems / 3);
D3DPT_TRIANGLELIST標(biāo)記將命令Direct3D畫不連續(xù)的三角形。你指定從索引的第0個(gè)頂點(diǎn)開始,指定所要畫的三
角形數(shù)目。
如果正確的話,你會(huì)看到一個(gè)三角形畫在先前的綠色背景窗口上。
聲明:
本文的英文原作者是:Toby "Ace" Jones 作者電子郵件:tjones@hot-shot.com
我首先看到英文原作的網(wǎng)站是:http://gamedev.net
本文的SourceCode和英文原作都可以從http://gamedev.net得到
歡迎您光臨我的主頁(yè):http://gamedev.363.net
陳偉凡
E-mail: laical@21cn.com
2000/12/19
總結(jié)
以上是生活随笔為你收集整理的转:DirectX8.0初体验, 有点老,但写的挺好的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 家庭记账本开发记录(6)
- 下一篇: 一辆车到底需要多少芯片?