由于Aug 8造成的D3D9恐懼癥已經(jīng)完全消除了,這一章將會(huì)給大家介紹將3D引擎轉(zhuǎn)向D3D9的各個(gè)方面,包括終于出現(xiàn)的全屏幕模式。從這章以后,我將使用D3D9作為講解的語言繼續(xù)D2D教程。
【OP結(jié)束,開始正片】
『Why?』
估計(jì)大家首先要問的就是“Why?”為什么要前進(jìn)到D3D9?理由如下:
1、D3D9修復(fù)了D3D8已知的所有Bug,因此運(yùn)行起來更穩(wěn)定,速度也要快。
2、D3D9提供了許多便利的新功能,雖然絕大多數(shù)是面向3D的,但是也有不少2D適用的,比如IDirect3DDevice9::StretchRect,以及對(duì)IDirect3DSurface9的改進(jìn)等等。D3DX庫就更多了,比如D3DXSaveSurfaceToFileInMemory,一開始沒發(fā)現(xiàn)這個(gè)函數(shù)有啥用處,現(xiàn)在基本離不開了。
3、HLSL。就像上一話我說的那樣,D2D教程以后會(huì)有PixelShader的內(nèi)容。我可不想拿匯編來寫Shader,會(huì)死人的(祝賀我吧,終于拋棄匯編Shader了……)。雖然說這不是決定性的理由,因?yàn)檫€有Cg什么的,不過我想編寫顯卡無關(guān)的代碼,因此我不去研究Cg(反正和HLSL差不多)以及R2VB之類。
4、ID3DXFont,往下看你就知道了。
《D3D的變化》
『界面名稱變化』
一句話:8改成9就行。
『“創(chuàng)建”型方法的一個(gè)統(tǒng)一變化』
許多Create*()方法,比如創(chuàng)建設(shè)備、創(chuàng)建紋理、創(chuàng)建頂點(diǎn)緩沖等等,多了一個(gè)HANDLE* pSharedHandle參數(shù),無用,NULL之(看來微軟原打算弄個(gè)共享句柄之類,不過被D3D10巨大的變化浮云了)
『創(chuàng)建D3D設(shè)備的變化』
D3DPRESENT_PARAMS的FullScreen_PresentationInterval變成了PresentationInterval,也就是說即使在窗口模式下也可以做到垂直同步來防止撕裂現(xiàn)象(2D的福音啊)。相應(yīng)的,D3DSWAPEFFECT_COPY_VSYNC消失了,反正這個(gè)效果也不咋的,消失了也好。
要做到垂直同步需要給PresentationInterval賦值D3DPRESENT_INTERVAL_DEFAULT或D3DPRESENT_INTERVAL_ONE。其中D3DPRESENT_INTERVAL_ONE的效果比D3DPRESENT_INTERVAL_DEFAULT好一點(diǎn),不過相應(yīng)的也會(huì)占用多一點(diǎn)點(diǎn)系統(tǒng)資源……真的只有一點(diǎn)點(diǎn)而已,實(shí)在是無所謂的……
如果不要垂直同步,想要看看實(shí)際禎速的話,D3DPRESENT_INTERVAL_IMMEDIATE。
注意在窗口模式下,你只能使用這三種Present模式,全屏幕模式下就可以使用別的(但是要首先檢測(cè)D3DCAPS9以查看顯卡是否支持)。不過我感覺對(duì)99%的游戲來說,有這三個(gè)就足夠了。
另外在窗口模式下,BackBufferFormat也可以設(shè)置成D3DFMT_UNKNOWN,D3D會(huì)自動(dòng)獲取當(dāng)前桌面的格式設(shè)定成后備緩沖的格式,省去GetDisplayMode。實(shí)際上,窗口模式下的后備緩沖已經(jīng)不需要和桌面格式相同,你可以通過IDirect3D9::CheckDeviceFormatConversion來檢查,如果這個(gè)設(shè)備支持這兩種顏色格式之間的轉(zhuǎn)換,就可以給程序的后備緩沖設(shè)定上不同的格式。我試過在桌面格式為32Bit(D3DFMT_X8R8G8B8)時(shí)將程序的后備緩沖格式設(shè)置為D3DFMT_R5G6B5(16Bit),發(fā)現(xiàn)了速度提升,也就是說這個(gè)設(shè)定是有意義的。
可創(chuàng)建的設(shè)備類型多了一種D3DDEVTYPE_NULLREF,在安裝了D3D SDK的機(jī)子上等同于D3DDEYTYPE_REF,在其他的機(jī)子上,這種設(shè)備實(shí)際上沒有創(chuàng)建真正意義的D3D設(shè)備,只是允許你創(chuàng)建的紋理、表面等資源,但是Render、Present等操作都會(huì)無效(實(shí)際上這些資源都創(chuàng)建在了D3DPOOL_SCRATCH池里,不管你設(shè)定使用的是什么POOL)。也就是說,僅僅在模擬基本的運(yùn)行而已。你可以用這個(gè)設(shè)備來編寫一個(gè)利用D3DX函數(shù)庫進(jìn)行圖像格式轉(zhuǎn)換的程序,比如把一大堆不同的格式轉(zhuǎn)換成易于D3D9使用的DDS格式。因?yàn)閷?shí)際上沒有創(chuàng)建設(shè)備,你甚至可以編寫成控制臺(tái)的,通過GetConsoleWindow的方法獲得HWND。Mercury 3用的MIF格式的轉(zhuǎn)換器就是這么做出來的。注意D3DDEVTYPE_NULLREF只能用在IDirect3D::CreateDevice時(shí),其他的方法都不行。
『創(chuàng)建表面的變化』
創(chuàng)建表面(Surface)的方法變成了IDirect3DDevice9::CreateOffscreenPlainSurface,參數(shù)很簡單不用多說,需要注意的是可以選擇POOL了。
『設(shè)定FVF的變化』
設(shè)定FVF時(shí),原來通過IDirect3DDevice8::
SetVertexShader,現(xiàn)在有了一個(gè)專門用來設(shè)定FVF的方法:IDirect3DDevice9::
SetFVF。這是個(gè)很好的變化,省得把FVF和Shader弄混(題外話:也就是因?yàn)檫@個(gè)變化,讓Shader在設(shè)備Re
set后得以保存,不錯(cuò)不錯(cuò))
『獲取后備緩沖』
D3D9現(xiàn)在允許有多個(gè)后備緩沖交換鏈,不過對(duì)于2D來說,基本不需要這種東西,IDirect3DDevice9::GetBackBuffer多出來的第一個(gè)參數(shù)賦值0即可。如果你有興趣,可以去研究一下這個(gè)玩意,有時(shí)候可以用來做分場。
『
SetStreamSource』
這個(gè)方法的功能被擴(kuò)展了,對(duì)比參數(shù)就可以知道,多出來的Off
setInBytes允許你選擇一個(gè)頂點(diǎn)緩沖的Off
set,D3D9將從這個(gè)Off
set之后開始讀取數(shù)據(jù)。因此你可以把幾組用來渲染紋理的正方形頂點(diǎn)存儲(chǔ)到一個(gè)頂點(diǎn)緩沖里面。
『
SetSamplerState』
這個(gè)是D3D9的新方法,把原先
SetTextureStageState的一些功能獨(dú)立了出來,和2D關(guān)系最密切的就是紋理過濾了。原先的D3DTSS_MINFILTER變成了D3DSAMP_MINFILTER,相應(yīng)的D3DTSS_MAGFILTER也變成D3DSAMP_MAGFILTER,D3DTSS_MAXANISOTROPY變成D3DSAMP_MAXANISOTROPY。另外還有更多的,比如紋理尋址等。你去看一下D3DSAMPLERSTATETYPE枚舉類型的內(nèi)容就知道它“遷移”了些什么。
這個(gè)變化對(duì)于Shader來說很方便。改成Sampler的東西在PixelShader過程也會(huì)有效,而沒有更改的東西在PixelShader就不會(huì)有效了。D3D8時(shí)候把這些全都放在了一起,容易造成混亂。
『
SetRenderTarget』
D3D9現(xiàn)在允許多重RenderTarget存在,不過我們基本上只用一個(gè),RenderTargetIndex設(shè)為0,第二個(gè)參數(shù)仍然是需要設(shè)定的表面。與D3D8相同的是,在設(shè)定之前仍然需要先通過GetSurfaceLevel獲得表面才行。
『頂點(diǎn)緩沖的鎖定』
注意IDirect3DVertexBuffer9::Lock的第三個(gè)參數(shù),從原來的BYTE**變成了void**。也就是這樣了……
『其他的一些變化』
1、CopyRects變成了UpdateSurface。和UpdateTexture一樣,只能從D3DPOOL_SYSTEMMEM拷貝到D3DPOOL_DEFAULT
2、增加了一個(gè)比較有用的IDirect3DDevice9::ColorFill方法,作用是向D3DPOOL_DEFAULT的某個(gè)區(qū)域填充顏色,和Clear的功能類似,但是在使用目的上要比Clear明確的多,并且由于不牽扯深度緩沖之類,速度要快一些。
3、增加了一個(gè)IDirect3DDevice9::StretchRect方法,通過這個(gè)方法就可以在D3DPOOL_DEFAULT的表面或紋理之間進(jìn)行帶過濾器的縮放操作,免去利用Render的過程,非常有用。不過這個(gè)方法由于使用了硬件處理,限制較多,請(qǐng)大家仔細(xì)看SDK文檔的Remarks部分。
《D3DX的變化》
D3DX的變化實(shí)際上相當(dāng)?shù)亩啵缥乙婚_始所說,基本都是面向3D的。需要我們注意的有以下幾種:
1、D3DX***FromFile之類的函數(shù)支持的圖像格式增加了,不過所增加的都是很少見的格式。平時(shí)基本上還是用BMP、TGA和PNG就足夠。
2、增加了D3DXSave***ToFileInMemory,將會(huì)把文件寫入內(nèi)存。這個(gè)函數(shù)的作用似乎不是很容易想到,但是如果你要寫一個(gè)集成了轉(zhuǎn)換、打包功能的工具,這個(gè)就很有用了,省去了通過臨時(shí)文件操作造成的各種問題。另外如果你熟悉某種圖形文件的格式的話,還可以通過直接訪問這個(gè)文件獲得RAW信息。注意,這類函數(shù)寫入的是一個(gè)ID3DXBuffer,這個(gè)東西很簡單,只有兩個(gè)特定的方法,一看便懂,不再多言。
3、增加了一個(gè)ID3DXLine,可以方便你在2D上畫線,創(chuàng)建ID3DXLine的方法是D3DXCreateLine。這個(gè)東西也不復(fù)雜,使用方法有點(diǎn)像ID3DXSprite,稍微研究一下就能弄懂,注意每次Draw的是D3DPT_LINESTRIP。用它比直接用頂點(diǎn)緩沖的好處是可以方便的打開反鋸齒,效果嘛……基本滿意。
4、增加了一個(gè)ID3DXRenderToSurface,“理論上來說”方便了利用RenderTarget的過程……不過我感覺反而弄得復(fù)雜了。創(chuàng)建的方法是D3DXCreateRenderToSurface,有心情的朋友自己研究看看吧,我就不講了。
ID3DXSprite和ID3DXFont在Summer 2004的DX9 SDK(也就是第一版DX9.0c)開始發(fā)生了很大變化,下面詳述:
『ID3DXSprite』
你會(huì)發(fā)現(xiàn)ID3DXSprite::DrawTransform不見了,取而代之的是其功能被整合到ID3DXSprite::
SetTransform里面,也就是說為了縮放和旋轉(zhuǎn),我們不得不和矩陣打交道了。其實(shí)也不會(huì)太復(fù)雜,因?yàn)槲覀冎皇亲鲆恍┚仃囘\(yùn)算,學(xué)過線性代數(shù)的朋友肯定會(huì)很熟悉,就算你不怎么熟悉線性代數(shù),也沒關(guān)系,D3DX函數(shù)庫提供了現(xiàn)成的矩陣運(yùn)算函數(shù),你只要用就行了。
D3DXMatrixScaling
D3DXMatrixRotationZ
D3DXMatrixTranslation
按照順序調(diào)用這三個(gè)函數(shù)……或許學(xué)過3D的馬上就想到這點(diǎn)了,的確是沒錯(cuò)啦。注意順序哦:Scaling -> Rotation -> Translation,簡稱SRT(看過全金屬狂潮嗎?看過的話這個(gè)單詞很好記吧^_^),弄錯(cuò)了可是得不到正確結(jié)果的。
你是不是想到把同一個(gè)D3DXMATRIX當(dāng)作參數(shù)使用三次?錯(cuò)啦!你要用矩陣乘法。創(chuàng)建三個(gè)D3DXMATRIX,比如mat1、mat2、mat3,分別用這三個(gè)函數(shù)將其創(chuàng)建為縮放矩陣、旋轉(zhuǎn)矩陣和平移矩陣,然后在ID3DXSprite::
SetTransform時(shí),這樣寫:
SetTransform(mat1 * mat2 * mat3);
有夠麻煩的是不?ID3DXSprite方便了做3D的,可害苦了做2D的,所以我已經(jīng)不直接用這個(gè)了(什么叫不直接用?往下看)。
『ID3DXFont』
大家來歡呼吧!Summer 2004改進(jìn)的ID3DXFont徹底槍斃掉了上一話那個(gè)字體引擎……
這東西的改進(jìn),怎么說呢,應(yīng)該說是改頭換面吧,速度、效果都和以前不是一個(gè)數(shù)量級(jí)。可憐的PixelFont,才存在了一話就要被拋棄了。
ID3DXFont多出來的幾個(gè)方法,Preload*()這類的,就是把一些常用的字的字模提前讀取到內(nèi)存里面加快速度,同時(shí)還可以使用ID3DXSprite渲染,進(jìn)一步加快速度。雖然內(nèi)部仍然有GDI的部分,不過很明顯工作方式發(fā)生了極大的變化。根據(jù)我的估計(jì),這次的ID3DXFont很聰明的利用GDI獲得文字的輪廓,然后通過紋理來渲染。這樣的速度就快得多了,而且文字質(zhì)量也得到了很好的控制,基本和直接用GDI的質(zhì)量相同了。
由于PreloadCharacters()和PreloadGlyphs()不是那么好理解,一般用PreloadText()就行。建議將所有ASCII字符、標(biāo)點(diǎn)符號(hào)和部分漢字預(yù)讀進(jìn)去。這個(gè)預(yù)讀過程略微有點(diǎn)慢,而且根據(jù)預(yù)讀的文字?jǐn)?shù)量和你創(chuàng)建文字的字號(hào),占用的內(nèi)存也不同。這里給大家一堆文字,你Copy過去就行:
引用
const char strPreloadText[] = " 1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM~!@#$%^&*()-=[]\\;',./_+{}|:\"<>? 、。·ˉˇ¨〃—~‖…‘’“”〔〕〈〉《》「」『』〖〗【】!"#¥%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}我人有的和主產(chǎn)不為這工要在第一上是中國經(jīng)已發(fā)了民同";
注意第一個(gè)字符是空格哦!把空格預(yù)讀進(jìn)去可是很重要的^_^
看上去并不多,因?yàn)橐紤]到內(nèi)存占用及速度,我只預(yù)讀了一些符號(hào)和五筆的一鍵字。這些字符在24號(hào)字時(shí)候已經(jīng)占用了快1MB了,比起PixelFont字庫占用的要大得多。天知道ID3DXFont到底預(yù)讀了些什么……
PreloadText()的第二個(gè)參數(shù)不要用strlen,sizeof(strPreloadText)即可。
然后就是利用ID3DXSprite來渲染。注意ID3DXFont::DrawText的第一個(gè)參數(shù)就是LPD3DXSPRITE,因此如果要利用ID3DXSprite,要將ID3DXFont::DrawText放到ID3DXSprite::Begin和ID3DXSprite::End之間。這就是我剛才說的不直接用ID3DXSprite的意思,ID3DXFont會(huì)完成ID3DXSprite的全部調(diào)用,你不用擔(dān)心。
另外你應(yīng)該注意到ID3DXSprite::Begin增加了參數(shù),實(shí)際上DX文檔里面沒說,但是示例里面有,如果想讓ID3DXSprite發(fā)揮作用并且最大幅度的提升效率,參數(shù)上設(shè)定D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE即可。意思很明白:打開Alpha過濾和紋理篩選。這里DX文檔上有個(gè)錯(cuò)誤一直沒改:文檔里給出的是D3DXSprite__SORT_TEXTURE,但是你可以試試,絕對(duì)報(bào)錯(cuò)。
剩下的就沒啥了,ID3DXFont的使用方法上一話已經(jīng)講過。要注意的是D3DXCreateFont和D3DXCreateFontIndirect都發(fā)生了變化。D3DXCreateFont已經(jīng)不再牽扯GDI了,D3DXCreateFontIndirect所使用的結(jié)構(gòu)也變成了D3DXFONT_DESC,相對(duì)于LOGFONT結(jié)構(gòu),除去了一些用不著的參數(shù),增加了一個(gè)MipLevels,就是MipMap等級(jí)啦,不用多說,2D下只用1。其他的上一話都有。實(shí)際上由于D3DXCreateFont已經(jīng)不再關(guān)聯(lián)GDI,D3DXCreateFontIndirect的存在僅僅是由于歷史原因(為了兼容像我這種人的使用習(xí)慣),大家還是用D3DXCreateFont吧,省事。
截圖就不貼了,沒啥意義。你可能覺得直接向后備緩沖上DrawText還不夠好看,那么就先畫到一張紋理上,然后將紋理錯(cuò)位渲染到后備緩沖并且打開線型過濾,就可以達(dá)到和PixelFont相同的效果了。
速度嘛……我畫了整整一屏幕字,在不緩沖文字的情況下(這個(gè)“緩沖文字”和ID3DXFont的文字緩沖可不是一回事啊!看過上一話的都應(yīng)該知道我這里指的是什么),速度仍然在120FPS以上。或許你會(huì)覺得速度還是有點(diǎn)慢,但是,如果用D3D8的ID3DXFont畫上這么一屏幕,基本就只剩20FPS了。
使用ID3DXFont替換掉PixelFont的優(yōu)勢(shì)就是可以方便的自定義字體字號(hào)了,并且也不再受GB2312字庫的限制。所以大家都換了吧……都換了吧……把PixelFont忘了吧……
『穩(wěn)定的DX9 SDK版本』
我現(xiàn)在用的是April 2006,而且應(yīng)該會(huì)用很長時(shí)間。August 2006我是肯定不會(huì)去用啦!即使我不再恐懼D3D9,也會(huì)對(duì)這個(gè)SDK避讓三分的。其實(shí)對(duì)于2D,我感覺用到April 2006就足夠了,之后的DX9 SDK主要在D3DX的3D函數(shù)庫部分進(jìn)行更改……其實(shí)也是秋后的螞蚱蹦達(dá)不了幾天,D3D10馬上就要出來了。要說D3D10啊……你還是看我另外一篇日志好了,總之打死我都不拿它做2D。
實(shí)際上僅僅是2D的話,從D3D8轉(zhuǎn)向D3D9并沒有多少變化,主要是穩(wěn)定嘛!只要你不調(diào)用一些D3D9專用的功能,即使拿D3D9來做2D,在絕大多數(shù)顯卡上還是能夠運(yùn)行的。嗯……GF2等級(jí)以上吧,GF2之前的,也太老了,無視好了。
《再上點(diǎn)菜好了:全屏幕模式》
其實(shí)并不是多么復(fù)雜的問題,讓我拖了這么久……不拖了,這里就教給大家如何做全屏幕模式以及如何處理設(shè)備丟失的問題。
『創(chuàng)建全屏幕模式』
D3DPRESENT_PARAMS里面,Windowed設(shè)定為false,并且一定要設(shè)定BackBufferWidth和BackBufferHeight,完畢。
哈哈,就這么簡單,或許早就有人嘗試過了,但是你試試按下Alt+Tab,再切換回去,保證你什么都看不到。
之前曾經(jīng)說過,DX8之前的版本,在全屏幕下工作比在窗口下容易,到DX8之后就則完全顛倒過來。因?yàn)樵诖翱谀J较虏挥脫?dān)心設(shè)備丟失(除非你更改桌面分辨率),全屏幕模式下就會(huì)有這個(gè)問題了。下面詳述:
『設(shè)備、資源丟失』
設(shè)備丟失會(huì)發(fā)生在全屏幕模式下切換回桌面時(shí)(不論是通過Alt+Tab還是QQ上有人給你發(fā)了張圖片-_-bbb),而且如果在調(diào)用IDirect3DDevice9::Re
set(從現(xiàn)在開始就是D3D9了啊!忘記D3D8吧……)的時(shí)候發(fā)生錯(cuò)誤,設(shè)備也會(huì)丟失。
設(shè)備丟失會(huì)造成資源丟失:所有創(chuàng)建在D3DPOOL_DEFAULT池的資源都會(huì)丟失,需要重新創(chuàng)建,其內(nèi)容當(dāng)然也會(huì)消失,需要重寫。
然而創(chuàng)建在D3DPOOL_SYSTEMMEM和D3DPOOL_SCRATCH池的資源不會(huì)受到影響。創(chuàng)建在D3DPOOL_MANAGED池的資源也不會(huì)丟失,而且在設(shè)備重新可用的時(shí)候,D3DPOOL_MANAGED池的資源也可以立即投入使用,內(nèi)容也不會(huì)改變。看這個(gè)池名字:托管池就能知道,D3D幫你處理了所有問題。
因此避免設(shè)備丟失后資源丟失的簡易方法就是將所有資源創(chuàng)建在D3DPOOL_MANAGED池內(nèi)。不過這并不是個(gè)好方法,這意味著不能用渲染對(duì)象——記得嗎?RenderTarget只能創(chuàng)建在D3DPOOL_DEFAULT。實(shí)際上最好的方法是跟蹤所有D3DPOOL_DEFAULT資源,比如利用std::list,將所有D3DPOOL_DEFAULT資源勾住,在設(shè)備發(fā)生丟失的時(shí)候釋放掉資源,設(shè)備可以繼續(xù)使用的時(shí)候重新創(chuàng)建資源,記得把數(shù)據(jù)寫回去。對(duì)于其他的池就不用這么折騰了。
『當(dāng)設(shè)備丟失之后』
不論通過任何方式發(fā)生了設(shè)備丟失,所有的操作幾乎都會(huì)失效,只有Release()可以用——其實(shí)D3D會(huì)保證有部分操作可以成功,但是也僅僅是“可以”成功而不是“一定”成功,所以你還不如認(rèn)定丟失的時(shí)候全都會(huì)失敗比較好——以及IDirect3DDevice9::TestCooperativeLevel。因此在設(shè)備丟失之后,你應(yīng)該停止整個(gè)游戲循環(huán),而通過反復(fù)調(diào)用IDirect3DDevice9::TestCooperativeLevel判斷設(shè)備是否可用。
『IDirect3DDevice9::TestCooperativeLevel』
這個(gè)方法檢測(cè)當(dāng)前的設(shè)備狀態(tài)。返回值有四種:D3D_OK一切正常,D3DERR_DEVICELOST設(shè)備丟失,D3DERR_DEVICENOTRE
SET設(shè)備可以Re
set。另外還有D3D9新增的D3DERR_DRIVERINTERNALERROR,遇到這個(gè)你就完蛋了,基本不可能恢復(fù)了,終止程序吧。
按照順序來講,如果游戲在正常運(yùn)行,D3D_OK會(huì)返回;如果發(fā)生了設(shè)備丟失并且在這個(gè)時(shí)候不能恢復(fù),比如全屏幕模式的時(shí)候用戶切換到了Windows桌面,就會(huì)返回D3DERR_DEVICELOST;如果用戶又切換回了游戲,設(shè)備可以恢復(fù)了(還沒恢復(fù)呢!只是“可以”恢復(fù)而已),就會(huì)返回D3DERR_DEVICENOTRE
SET。
另外,IDirect3DDevice9::Present也會(huì)返回類似的值,不過你最好別指望這個(gè),老老實(shí)實(shí)的用TestCooperativeLevel。因?yàn)镻resent在設(shè)備可以恢復(fù)的時(shí)候還是返回D3DERR_DEVICELOST(外一句:D3D10的時(shí)候TestCooperativeLevel就會(huì)完全整合到Present里面了,可喜可賀可喜可賀)
『處理設(shè)備丟失』
看下面的偽代碼:
switch (IDirect3DDevice9::TestCooperativeLevel()){
case D3D_OK:
GameLoop();
break;
case D3DERR_DEVICELOST:
break;
case D3DERR_DEVICENOTRE
SET OnLostDevice();
IDirect3DDevice9::Re
set();
OnRe
setDevice();
break;
default:
QuitGame();
break;
}
GameLoop()就是你的游戲運(yùn)行的過程了。把這個(gè)switch寫在我們游戲框架的GameMain()部分,具體的位置可以看任何一話附帶的源代碼。
好像我一直沒有講IDirect3DDevice9::Re
set的參數(shù)啊?因?yàn)橹挥幸粋€(gè)參數(shù),就是指向D3DPRESENT_PARAMS的指針。把你第一次創(chuàng)建設(shè)備時(shí)使用的D3DPRESENT_PARAMS結(jié)構(gòu)保存起來,供Re
set來用。
OnLostDevice()就是Release掉所有D3DPOOL_DEFAULT的資源,OnRe
setDevice()就是Create*()恢復(fù)啦!你可能注意到ID3DXFont、ID3DXSprite等等都有同名的方法,就是在這個(gè)時(shí)候調(diào)用的。如果你沒有這么做,也就是說還保留著任何D3DPOOL_DEFAULT的資源的話,IDirect3DDevice9::Re
set就一定會(huì)失敗。
另外在OnRe
setDevice里面你還要重新進(jìn)行
SetRenderState、
SetSamplerState等等,Re
set之后這些東西也丟失了。實(shí)際上Re
set和重新創(chuàng)建一次設(shè)備類似,所不同的是重新創(chuàng)建設(shè)備的話你需要連D3DPOOL_MANAGED的資源也Release掉。這個(gè)話題就不討論了。
從代碼可以看出來,D3DERR_DEVICELOST時(shí)程序什么都沒做,只是在傻等。我認(rèn)為這是一個(gè)好習(xí)慣,因?yàn)閷?shí)在不能保證在D3DERR_DEVICELOST時(shí)除了Release還能干什么,與其這樣還不如等設(shè)備能用了再說。
實(shí)在懶得管資源的話,全部D3DPOOL_MANAGED好了。至于渲染對(duì)象?自己想辦法。
『人工制造“設(shè)備丟失”』
“干嘛還要制造設(shè)備丟失啊?”如果更改游戲分辨率、色深、切換全屏幕及窗口狀態(tài),進(jìn)行這樣的操作也要通過Re
set,同樣的,Re
set之前也要釋放掉所有D3DPOOL_DEFAULT資源(其實(shí)嚴(yán)格來說,還有更多的資源也要釋放,不過在2D下基本不會(huì)創(chuàng)建這類資源,你就不用管了)并且調(diào)用ID3DXSprite::OnLostDevice之類的方法。這就是人工制造“設(shè)備丟失”了。實(shí)際上在這個(gè)過程設(shè)備并沒有真正的丟失,只是會(huì)有一段時(shí)間處于不可用的狀態(tài),此時(shí)Re
set尚未返回,整個(gè)D3D設(shè)備就好像死了一樣。舉個(gè)例子,你切換桌面分辨率,會(huì)有那么一段時(shí)間顯示器上什么都不顯示,然后很快就正常了。和這個(gè)現(xiàn)象是同一個(gè)原因。Re
set成功后記得恢復(fù)資源。
你可能注意到這里的Re
set和上面的Re
set不是一回事。的確是這樣,這里是為了重設(shè)狀態(tài)而不是恢復(fù)設(shè)備。因此更改分辨率、色深的Re
set需要寫到switch外面,也就是別和它攪和的意思-_-bb。而且你只需要OnLostDevice -> Re
set -> OnRe
setDevice。記住:正確的調(diào)用Re
set不會(huì)造成設(shè)備丟失,這個(gè)概念別弄混了。
『切換全屏幕模式時(shí)的注意事項(xiàng)』
注意WindowStyle的變化。切換成全屏幕模式后,只能使用WS_POPUP,不然顯示會(huì)變得怪怪的,你可以通過
SetWindowLongPtr函數(shù)更改窗口外觀,第二個(gè)參數(shù)指定GWL_STYLE即可。別忘了WS_VISIBLE啊!不然你什么都看不見。
『更詳細(xì)的文檔』
我這里只是簡單討論了造成設(shè)備丟失的原因及處理方法,更詳細(xì)的內(nèi)容你可以參考DX SDK文檔的Lost Device文章,人家是權(quán)威的。
【以上,正片結(jié)束,后面是ED】
我們前進(jìn)到了D3D9,趕上了時(shí)代。
我們創(chuàng)建了全屏幕游戲,趕上了時(shí)代。
我卻變得一腦子漿糊,被觀眾拋棄了。
哈哈,開玩笑啦,不過這一話很亂倒是真的,因?yàn)椴徽撌歉碌紻3D9還是設(shè)備丟失,牽扯的東西都太散太雜,結(jié)果弄得這一話也是一盤散沙(居然又沒有附帶代碼)。唉,大家就忍了吧,忍不了的話就來PIA我吧。
關(guān)于更新至D3D9更多的內(nèi)容,你可以參考SDK文檔的《Converting to Direct3D 9》。
【以上,ED結(jié)束,后面是……】
第一季完結(jié)了……
回過頭來看看,從第一話創(chuàng)建一個(gè)Windows窗口,到這一話的設(shè)備丟失,話題的層次一直在深入,現(xiàn)在已經(jīng)深入到了不再是“學(xué)習(xí)”而是“研究”的范圍。我也不再想僅僅是搞“教學(xué)”而是想和大家“討論”。不過第一季主要還是教學(xué)吧。能堅(jiān)持著看D2D教程到現(xiàn)在的,應(yīng)該基本能夠?qū)懗鐾暾?D Demo來了吧。如果有什么問題的話,歡迎提出,我在看到后會(huì)立刻回答的……只要你這個(gè)問題不太RP的話……
那么,第二季會(huì)是什么樣子?
第二季就不再是教學(xué)了,而開始我和大家的討論過程。第二季的第一話,也就是第09話,我將提供一些高級(jí)技巧給大家,并希望有興趣的朋友和我一起進(jìn)行這些技巧的研究。另外在第二季里面,我們還要?jiǎng)?chuàng)建一個(gè)2D圖形引擎。原來打算給大家講解Medux 2,不過現(xiàn)在感覺這東西實(shí)在小兒科,絕對(duì)會(huì)讓大家B4的。那么既然如此,干脆介紹Mercury 3好了,有意見無?
透漏一點(diǎn)下一話的內(nèi)容吧:模糊精度和多次紋理渲染,嘿嘿,聽上去挺高深的是不?實(shí)際上超級(jí)簡單,就看你能不能想到而已。
希望你在看完這一話之后,返回去再把前面的內(nèi)容看看,相信你會(huì)得到新的收獲。搞不好你還能抓出幾個(gè)Bug呢!因?yàn)槲沂窍氲绞裁磳懯裁矗瑳]個(gè)章法,Bug是難免的。