本篇文章中,我們將一起探討三維游戲中粒子系統的方方面面,首先對粒子系統的基本概念特性做一個全面的認知,然后我們依舊是把粒子系統封裝在一個C++類中,模擬了三維游戲中唯美的雪花飛揚的景象,讓我們之前的綜合三維游戲場景更加炫。依舊是放出一張本篇文章的配套程序的截圖:
?
這個帥氣的大天使為我們唯美的雪花飛揚示例程序平添了幾分霸氣有木有?
?PS:示例程序的源代碼在文章末尾提供下載
?
大家應該記得,我們之前也用GDI實現過雪花粒子系統,那個時候由于圖形庫GDI的限制,實現效果或多或少顯得有些拙劣,這篇文章中,我們在DirectX的幫助下,專門用粒子系統重新實現了唯美雪花的飛揚景象,算是在為強大的粒子系統正名吧。
?
?
?
?
一、對粒子系統的基本認知 1983年,奇才Reeves.V.T在它發表的論文《Particle Systems A Technique for?Modeling a Class of Fuzzy Objects》中首次提出了粒子系統的概念。從此,粒子系統就開始廣泛運用于計算機中各種模糊景物的模擬。經常使用粒子系統模擬的現象有火焰、爆炸、煙、水流、火花、落葉、云、霧、雪、塵、流星尾跡或者像發光軌跡這樣的抽象視覺效果等等。這些物體模型在計算機中往往很難用具體的形狀、大小來描述,但是我們可以通過粒子系統的思想,去描述組成這些物體的每個元素和它的變化。
一般情況下,粒子的幾何特征都十分的簡單,可以采用一個像素或者是一個小的多邊形來表示。需要注意的是,粒子系統的最大的缺陷是,當粒子數量達到很大的規模的時候,對運行時機器性能的要求會更加苛刻,如果機器的性能跟不上,就會顯得達不到實時的運行效果,俗話說,就是粒子太多了,我們的電腦跑不動了,就會很卡。
在許多三維建模及渲染包內部就可以創建、修改粒子系統,如 3D Studio Max、Maya 以及 Blender 等。這些編輯程序使藝術家能夠立即看到他們設定的特性或者規則下粒子系統的表現,另外還有一些插件能夠提供增強的粒子系統效果,例如 AfterBurn 以及用于流體的 RealFlow。而2D的粒子特效軟件中particleIllusion最為出色,因為他的渲染比一般的3D軟件快較為平面化。Combustion 這樣的多用途軟件或者只能用于粒子系統的 Particle Studio 等都可以用來生成電影或者視頻中的粒子系統。而目前,粒子系統技術被廣泛用于大型3D游戲地制作中。首先看幾張用粒子系統制作出來的效果圖吧:
?
然后下圖是DirectX SDK中自帶sample一個和粒子系統相關的非常華麗的demo,推薦大家去運行一下,如果你的DirectX SDK安裝在D盤,那么路徑就是
D:\Program Files\Microsoft DirectXSDK (Jun 2010)\Samples\C++\Direct3D11\NBodyGravityCS11
放幾張運行的截圖:
?
粒子系統通常有三個要素:群體性、統一性和隨機性。下面我們分別來簡單看一下:
?
群體性:粒子系統是由大量的可見元素構成的。因此用粒子系統描述一團煙霧是合情合理的,但是我們所用粒子系統去描述一粒煙霧顯然只有鬧笑話了。
統一性:粒子系統的每個元素有具有相同的表現規律。比如,雪花粒子系統中的每一片雪花,都是白色無暇、輕盈靈動的。如果雪花粒子系統中出現了彩色的粒子,那顯然就是異類了。
隨機性: 粒子系統中每個元素又隨機表現出不同的特性。比如煙霧中每一個煙霧粒子的運動軌跡往往都是雜亂無章的,但是對于整個煙霧系統來說,這些煙霧粒子往往都有一個大體的運動方向。
?
?
?
?
二、粒子系統的基本原理 ?
粒子通常都是一個帶有紋理的四邊形。我們通過這個使用了紋理映射的四邊形,可以認為粒子實際上是一個很小的網格模型,只不過是紋理賦予了它特殊的外表罷了。繪制粒子就如果繪制多邊形一樣簡單,因為一個粒子說白了就是一個可改變大小并映射了紋理的四邊形罷了。
下圖就是一個20個單位大小的粒子。
?
?
如果給出了粒子中心點的坐標和粒子的大小,不難計算出繪制粒子所需要的4個頂點坐標,這樣往往比直接給4個頂點坐標來得直觀和節省空間。
另外,很多情況下,因為一個例子是使用兩個三角形組成的一個矩形來表示的,所以通常需要使粒子四邊形始終面向觀察者,這就用到了我們在Direct3D中的廣告板(Billboard)技術,也叫公告版技術。公告版技術的基本原理在這里也提一下吧,后面有機會就專門用一次更新來講解。公告版技術的基本原理就是在渲染一個多邊形時,首先根據觀察方向構造一個旋轉矩陣,利用這個旋轉矩陣旋轉多邊形讓這個多邊形始終是面向觀察者的,如果觀察方向是不斷變化的,那么我們這個旋轉矩陣也要不斷進行調節。這樣,我們始終看到的是這個多邊形“最美好”的一面。這樣先讓多邊形面向觀察者,然后再渲染的技術,就是傳說中的廣告板(Billboard)技術。
我們知道,粒子系統都由大量的粒子構成,每個粒子都有一組屬性,如位置、大小以及紋理,還比如顏色、透明度、運動速度、加速度、自旋周期,生命周期等等屬性。一個粒子需要具有什么樣的屬性,當然是取決于具體的運用了。
另外,粒子屬性的初始值常常都是隨機值,而粒子的產生也常常是由位于空間中某個位置的粒子源產生的。
?
粒子系統在宏觀和微觀上都是隨時間不斷變化的,一套粒子系統在它生命周期的每一刻,一般都需完成以下的四步曲的工作:
?
1.產生新的粒子
這一步當中,我們會根據預定的要求,產生一定數目的新粒子。粒子的各項初始屬性都可以用rand函數來在一定的范圍內賦上隨機的值。
?
2.更新現有粒子的屬性
比如粒子有位置和移動速度,自旋速度等等屬性,這就需要在每一幀當中根據原來的粒子的位置、移動速度和自旋速度重新進行計算和賦值更新。
?
3.刪除已經消亡的粒子
這一步是可選的,具體情況具體分析,因為有些粒子系統中粒子是一直都存在的,沒有消亡一說。在規定了粒子生命周期的一套粒子系統中,就需要判斷每個粒子是否生命走到了盡頭,如果是的話,那么它就game over,消亡了,得用相關代碼把它從粒子系統中消除。
?
4.繪制出粒子
這步沒有的話什么都不是,不顯示出來叫什么粒子系統啊。人家可不管你在之前做了多少工作,算了多少東西,反正玩家是要看到最終的顯示效果的。
?
在Direct3D 8.0以后,我們可以通過一種稱為點精靈(Point Sprite)的特殊點元來描述粒子系統中的粒子。和一般點元不同的是,點精靈可以進行紋理映射并改變大小。點精靈的使用常常是伴隨著SetRenderState中第一個參數取如下的幾個值:
?
D3DRS_POINTSIZE = 154,D3DRS_POINTSIZE_MIN = 155,D3DRS_POINTSPRITEENABLE = 156,D3DRS_POINTSCALEENABLE = 157,D3DRS_POINTSCALE_A = 158,D3DRS_POINTSCALE_B = 159,D3DRS_POINTSCALE_C = 160,
?
另外,粒子系統中的一個重要要素是保存粒子的存儲結構。我們可以用數組,如果需要動態插入和刪除原始的話,就進一步使用鏈表用或者模板了。
需要注意的是,因為粒子系統中會有很多粒子需要不斷地產生、消亡,如果在每個粒子產生時都分配內存,或者在每個粒子消亡時都釋放內存,這顯然會造成巨大的資源開銷,非常不推薦。這里我們按鏈表這種方案來講解。我們通常采用的做法是,未雨綢繆,預先為所有的粒子分配內存,并將這些粒子保存到一個鏈表當中。當需要產生新的粒子時,從這個鏈表中取出所需數量的粒子,并將它們加入到渲染鏈表中,而當一個粒子消亡后,重新將它們放回到原鏈表中,并從渲染鏈表中刪除這些粒子。最后,在程序結束時,統一一次性釋放所有粒子所占的內存空間。這就是比較科學的做法。
呼,不講概念了,太悶了,開始準備動手做些東西吧。
?
?
?
?
三、雪花粒子系統的設計 ?
?
我們之前已經提到過,粒子系統可以模擬很多的現象,比如火焰、爆炸、煙、水流、火花、落葉、云、霧、雪、塵、流星尾跡或者發光軌跡。對于現象的模擬,粒子的特性往往需要根據模擬的現象的屬性來具體地設計。
?
對于我們今天要用粒子系統來模擬的雪花飛揚場景,有兩個比較特殊的地方:
1.在雪花飛揚場景中,不需要用點精靈或者公告版技術來讓粒子的四個頂點所在的面始終朝向觀察者,因為雪花飛舞起來是非常優雅的,會悠揚地繞著不同的軸打轉,用了公告板技術反而畫蛇添足,顯得不那么真實了。你見過圓圓的雪花始終面朝著你轉圈的嗎,見鬼了吧!
2.?在雪花飛揚場景中,可以不需要粒子的動態消亡與產生,可以讓雪花粒子在一定區域內下落,如果下落到Y軸的臨界區域,就把粒子的Y坐標設為預定的臨界最高點的Y坐標,就像粒子都是從這個地方產生的一樣,這樣就會模擬出源源不斷地下雪景象。其實,我們又在用慣用伎倆來欺騙玩家了。
就像SisMVG童鞋在淺墨上篇文章中評論的一樣“其實世界上最大的騙子團體就是程序員”。我們只是其實是在讓規定數量的雪花粒子不斷地在跑堂,到了終點再讓他們從起點重新開始跑,一遍一遍地,只要程序不停止運行,那么就永不止息。你以為你體驗了無數的雪花從你眼前呼嘯而過優雅下落的樣子,其實也就是那么來來回回PARTICLE_NUMBER= 8000個而已,哈哈。而這個PARTICLE_NUMBER就是下面我們要封裝雪花飛揚粒子系統的類中給粒子數量規定的宏。
?
好了,我們開始寫這個雪花粒子系統的內容吧。
?
首先依舊是寫出這個名為SnowParticleClass類的大體輪廓。
首先當頭棒喝怒寫4個宏,方便宏觀調控。這四個宏分別用于表示雪花粒子數量,雪花飛揚區域的長度,雪花飛揚區域的寬度,雪花飛揚區域的高度。
?
[cpp] ?view plaincopy print?
#define?PARTICLE_NUMBER??100000???//雪花粒子數量,顯卡不好、運行起來卡的的童鞋請取小一點。?? #define?SNOW_SYSTEM_LENGTH_X??20000???//下雪區域的長度?? #define?SNOW_SYSTEM_WIDTH_Z??????????20000???//下雪區域的寬度?? #define?SNOW_SYSTEM_HEIGHT_Y????20000???//下雪區域的高度?? ?
這里的PARTICLE_NUMBER雪花粒子數量我們取的10萬,那么后面我們寫出來的游戲場景中就有10萬個雪花粒子,當然,前提是你的顯卡是糕富帥,才禁得住10萬及以上的粒子數量。如果像淺墨這樣的顯卡明日黃花ATI Radeon HD 5730,取八萬的雪花粒子,跑起來幀數就只有10幀左右了,卡的飛起。當然,你取20萬的粒子數量,在下雪區域是在20000x20000x20000的區域中就是超級大暴雪了。。。建議這時候把長度和寬度調大一些,來讓這20萬的粒子的活動區域更大。
?
然后我們寫出雪花粒子的FVF靈活頂點格式。頂點屬性當然是頂點坐標加上紋理坐標了:
?
[cpp] ?view plaincopy print?
struct?POINTVERTEX?? {?? ???????floatx,?y,?z;???? ???????floatu,v?;??????????????? };?? #define?D3DFVF_POINTVERTEX(D3DFVF_XYZ|D3DFVF_TEX1)?? ?
?
?
接下來是雪花粒子的屬性結構體,想一想現實生活中的雪花有哪些特定的屬性呢?唯美的雪花,有特定的位置,會旋轉,有下降速度,樣子不同,嗯,好,那么我們就這樣寫:
?
?
[cpp] ?view plaincopy print?
struct?SNOWPARTICLE?? {?? ???????floatx,?y,?z;?????? ???????floatRotationY;????????? ???????floatRotationX;??????? ???????floatFallSpeed;??????? ???????floatRotationSpeed;??????? ???????int???TextureIndex;????? };?? ?
好,邊角廢料寫完了,下面正式來設計這個類吧。首先來看一看要寫哪些成員變量,LPDIRECT3DDEVICE9類型的設備接口指針m_pd3dDevice不能少吧,雪花粒子數組m_Snows要有吧,頂點緩存對象m_pVertexBuffer要有吧,保存不同雪花紋理樣式的雪花紋理數組m_pTexture要有吧,嗯,成員變量就這些。
然后看看要有哪些成員函數,構造函數,析構函數先顯式地寫出來,然后粒子系統初始化函數InitSnowParticle,粒子系統更新函數UpdateSnowParticle,粒子系統渲染函數RenderSnowParticle,嗯,成員函數也就是這些了。整體來看,這里類的輪廓就是如下,即貼出SnowParticleClass.h的全部代碼:
?
[cpp] ?view plaincopy print?
??? ??? #pragma?once?? #include?"D3DUtil.h"?? #define?PARTICLE_NUMBER??20000???//雪花粒子數量,顯卡不好、運行起來卡的童鞋請取小一點。?? #define?SNOW_SYSTEM_LENGTH_X??20000???//下雪區域的長度?? #define?SNOW_SYSTEM_WIDTH_Z??????????20000???//下雪區域的寬度?? #define?SNOW_SYSTEM_HEIGHT_Y????20000???//下雪區域的高度?? ??? struct?POINTVERTEX?? {?? ???????floatx,?y,?z;???? ???????floatu,v?;??????????????? };?? #define?D3DFVF_POINTVERTEX(D3DFVF_XYZ|D3DFVF_TEX1)?? ??? ??? struct?SNOWPARTICLE?? {?? ???????floatx,?y,?z;?????? ???????floatRotationY;????????? ???????floatRotationX;??????? ???????floatFallSpeed;??????? ???????floatRotationSpeed;??????? ???????int???TextureIndex;????? };?? ??? class?SnowParticleClass?? {?? private:?? ???????LPDIRECT3DDEVICE9????????????????????????????m_pd3dDevice;????????????????? ???????SNOWPARTICLE????????????????????????????????????m_Snows[PARTICLE_NUMBER];???? ???????LPDIRECT3DVERTEXBUFFER9???m_pVertexBuffer;?????? ???????LPDIRECT3DTEXTURE9??????????????????m_pTexture[6];?? ??? public:?? ???????SnowParticleClass(LPDIRECT3DDEVICE9pd3dDevice);??? ???????~SnowParticleClass();?????????????????????????????????? ???????HRESULTInitSnowParticle();???????? ???????HRESULTUpdateSnowParticle(?float?fElapsedTime);??? ???????HRESULTRenderSnowParticle(?);??? };?? ?
?
?
?
四、雪花粒子系統的實現 ?
又到了做填空題的時候,對著上面我們寫勾勒出來的SnowParticleClass類,我們有5個函數需要填上實現代碼,還等什么,我們開始吧。
首先呢,構造函數:
?
[cpp] ?view plaincopy print?
SnowParticleClass::SnowParticleClass(LPDIRECT3DDEVICE9pd3dDevice)?? {?? ??????? ???????m_pd3dDevice=pd3dDevice;?? ???????m_pVertexBuffer=NULL;??? ???????for(inti=0;?i<5;?i++)?? ??????????????m_pTexture[i]=?NULL;?? }?? ?
?
接下來,粒子系統初始化函數InitSnowParticle()。首先呢,調用srand重新播種一下隨機數種子。然后for循環為所有的雪花粒子賦予獨一無二的各項屬性值。接著,用講爛了的頂點緩存使用五步曲的其中的三步為代表著所有雪花粒子屬性的一個頂點緩存賦值,最后調用6次D3DXCreateTextureFromFile從文件加載6種不同的雪花紋理進來。這6種雪花紋理圖是淺墨按照素材PS出來,分別導出的,效果圖在下面,還不錯,各有特點,非常漂亮:
?
??????????
?
?
經過上面的思考,InitSnowParticle()函數的實現代碼我們就知道怎么寫了:
?
[cpp] ?view plaincopy print?
HRESULTSnowParticleClass::InitSnowParticle(?)?? {?? ??????? ???????srand(GetTickCount());?? ???????for(inti=0;?i<PARTICLE_NUMBER;?i++)?? ???????{??????? ??????????????m_Snows[i].x????????=float(rand()%SNOW_SYSTEM_LENGTH_X-SNOW_SYSTEM_LENGTH_X/2);?? ??????????????m_Snows[i].z????????=?float(rand()%SNOW_SYSTEM_WIDTH_Z-SNOW_SYSTEM_WIDTH_Z/2);?? ??????????????m_Snows[i].y????????=?float(rand()%SNOW_SYSTEM_HEIGHT_Y);?? ??????????????m_Snows[i].RotationY?????=?(rand()%100)/50.0f*D3DX_PI;?? ??????????????m_Snows[i].RotationX???=?(rand()%100)/50.0f*D3DX_PI;?? ??????????????m_Snows[i].FallSpeed???=?300.0f?+?rand()%500;?? ??????????????m_Snows[i].RotationSpeed???=?5.0f?+?rand()%10/10.0f;?? ??????????????m_Snows[i].TextureIndex=?rand()%6;?? ???????}?? ??? ??? ??????? ???????m_pd3dDevice->CreateVertexBuffer(4*sizeof(POINTVERTEX),?0,?? ??????????????D3DFVF_POINTVERTEX,D3DPOOL_MANAGED,&m_pVertexBuffer,?NULL?);?? ??? ??????? ???????POINTVERTEXvertices[]?=?? ???????{?? ??????????????{-20.0f,?0.0f,?0.0f,???0.0f,?1.0f,?},?? ??????????????{-20.0f,?40.0f,?0.0f,???0.0f,?0.0f,?},?? ??????????????{??20.0f,?0.0f,?0.0f,???1.0f,?1.0f,?},?? ??????????????{??20.0f,?40.0f,?0.0f,???1.0f,?0.0f,?}?? ???????};?? ??????? ???????VOID*pVertices;?? ???????m_pVertexBuffer->Lock(0,?sizeof(vertices),?(void**)&pVertices,?0?);?? ??????? ???????memcpy(pVertices,?vertices,?sizeof(vertices)?);?? ??????? ???????m_pVertexBuffer->Unlock();?? ??? ??????? ???????D3DXCreateTextureFromFile(m_pd3dDevice,?L"GameMedia\\snow1.jpg",?&m_pTexture[0]?);?? ???????D3DXCreateTextureFromFile(m_pd3dDevice,?L"GameMedia\\snow2.jpg",?&m_pTexture[1]?);?? ???????D3DXCreateTextureFromFile(m_pd3dDevice,?L"GameMedia\\snow3.jpg",?&m_pTexture[2]?);?? ???????D3DXCreateTextureFromFile(m_pd3dDevice,?L"GameMedia\\snow4.jpg",?&m_pTexture[3]?);?? ???????D3DXCreateTextureFromFile(m_pd3dDevice,?L"GameMedia\\snow5.jpg",?&m_pTexture[4]?);?? ???????D3DXCreateTextureFromFile(m_pd3dDevice,?L"GameMedia\\snow6.jpg",?&m_pTexture[5]?);?? ??? ???????returnS_OK;?? }?? ?
?
接著我們來看一下粒子系統更新函數UpdateSnowParticle怎么實現。其實非常簡單,就是一個for循環遍歷所有的粒子,看有哪些需要更新的就可以了,對于我們雪花粒子系統,需要更新一下每個粒子的Y坐標,然后判斷是否到了“地面”,然后還要改變其自旋角度。那么,代碼寫出來就是這樣了:
?
[cpp] ?view plaincopy print?
HRESULTSnowParticleClass::UpdateSnowParticle(?float?fElapsedTime)?? {?? ??? ??????? ???????for(inti=0;?i<PARTICLE_NUMBER;?i++)?? ???????{?? ??????????????m_Snows[i].y-=?m_Snows[i].FallSpeed*fElapsedTime;?? ??? ?????????????? ??????????????if(m_Snows[i].y<0)?? ?????????????????????m_Snows[i].y=?SNOW_SYSTEM_WIDTH_Z;?? ?????????????? ??????????????m_Snows[i].RotationY????+=?m_Snows[i].RotationSpeed?*?fElapsedTime;?? ??????????????m_Snows[i].RotationX??+=?m_Snows[i].RotationSpeed?*?fElapsedTime;?? ???????}?? ??? ???????returnS_OK;?? }?? ?
?
?
最后來看一下最關鍵的粒子系統渲染函數RenderSnowParticle怎么寫。首先禁用照明,然后設置紋理狀態,接著設置設置Alpha混合系數,設置背面消隱模式為不剔除,然后就開始渲染了。需要注意的是設置Alpha混合系數這里依舊是我們之前沒有專門花一篇文章講,這里理解它的功能為進行透明貼圖就行了。就是我們在寫GDI游戲小程序的時候一直在做的糾結事情,把圖片背景的黑邊去掉。好了,思路有了,寫代碼還會難嗎:
?
?
[cpp] ?view plaincopy print?
HRESULT?SnowParticleClass::RenderSnowParticle(??)?? {?? ??????? ???????m_pd3dDevice->SetRenderState(D3DRS_LIGHTING,?false?);?? ??? ??????? ???????m_pd3dDevice->SetTextureStageState(0,?D3DTSS_COLOROP,??D3DTOP_SELECTARG1);??? ???????m_pd3dDevice->SetTextureStageState(0,?D3DTSS_COLORARG1,?D3DTA_TEXTURE?);?? ???????m_pd3dDevice->SetSamplerState(0,?D3DSAMP_MINFILTER,?D3DTEXF_LINEAR?);? ???????m_pd3dDevice->SetSamplerState(0,?D3DSAMP_MAGFILTER,?D3DTEXF_LINEAR?);? ??? ??????? ???????m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);??? ???????m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);??? ???????m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);?? ??? ??????? ???????m_pd3dDevice->SetRenderState(D3DRS_CULLMODE,?D3DCULL_NONE?);?? ??? ??????? ???????for(inti=0;?i<PARTICLE_NUMBER;?i++)?? ???????{?? ?????????????? ??????????????staticD3DXMATRIX?matYaw,?matPitch,?matTrans,?matWorld;?? ??????????????D3DXMatrixRotationY(&matYaw,m_Snows[i].RotationY);?? ??????????????D3DXMatrixRotationX(&matPitch,m_Snows[i].RotationX);?? ??????????????D3DXMatrixTranslation(&matTrans,m_Snows[i].x,?m_Snows[i].y,?m_Snows[i].z);?? ??????????????matWorld=?matYaw?*?matPitch?*?matTrans;?? ??????????????m_pd3dDevice->SetTransform(D3DTS_WORLD,??&matWorld);?? ??? ?????????????? ??????????????m_pd3dDevice->SetTexture(0,?m_pTexture[m_Snows[i].TextureIndex]?);??? ??????????????m_pd3dDevice->SetStreamSource(0,m_pVertexBuffer,?0,?sizeof(POINTVERTEX));? ??????????????m_pd3dDevice->SetFVF(D3DFVF_POINTVERTEX);???? ??????????????m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,?2);??? ??? ???????}?? ??? ??????? ???????m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,false);?? ???????m_pd3dDevice->SetRenderState(D3DRS_CULLMODE,?D3DCULL_CCW?);?? ???????m_pd3dDevice->SetRenderState(D3DRS_LIGHTING,?true?);?? ??? ???????returnS_OK;?? }?? ?
?
析構函數就很簡單了,就是在收拾殘局,看有什么COM接口要釋放的:
?
[cpp] ?view plaincopy print?
SnowParticleClass::~SnowParticleClass()?? {?? ???????SAFE_RELEASE(m_pVertexBuffer);?? ??? ???????for(inti=0;i<3;?i++)?? ???????{?? ??????????????SAFE_RELEASE(m_pTexture[i]);?? ???????}?? ??? ?
五,雪花飛揚粒子類的使用 與類打交道封裝功能和我們經歷的生活一樣,也是一個先苦后甜的過程。
設計和實現這個類的時候或許是苦澀的,但是先苦后甜是必須的,寫完這個類之后,用起來非常的方便,只用幾行代碼而已,一個唯美的雪花飛揚景象就加入到我們的游戲場景中了。。
也就是如下的三步:
?
Ⅰ.首先,定義一個SnowParticleClass類的全局指針實例:
?
[cpp] ?view plaincopy print?
SnowParticleClass*?????????????????g_pSnowParticles=?NULL;????????????? ?
?
Ⅱ.然后,在初始化階段拿著雪花飛揚類的指針對象SnowParticleClass到處“指”,創建并初始化粒子系統:
?
[cpp] ?view plaincopy print?
????g_pSnowParticles=?new?SnowParticleClass(g_pd3dDevice);?? ????g_pSnowParticles->InitSnowParticle();?? ?
?
?
Ⅲ.最后,就是在Render函數中依然是拿著天空類的指針對象g_pSnowParticles先指一下UpdateSnowParticle函數,更新粒子系統,然后再指一下RenderSnowParticle函數,進行渲染。
?
[cpp] ?view plaincopy print?
??????g_pSnowParticles->UpdateSnowParticle(fTimeDelta);?? ??????g_pSnowParticles->RenderSnowParticle();?? ?
? ? ??
另外需要注意上面更新粒子系統的UpdateSnowParticle函數我們用到了一個流逝時間參數fTimeDelta,所以我們就要把我們服役多年的消息循環中改成如下含有流逝時間的更加先進的消息循環體系,然后讓Direct3D_Update和Direct3D_Render各增加一個代表流逝時間的fTimeDelta參數。
?
[cpp] ?view plaincopy print?
???????MSGmsg?=?{?0?};?? ???????while(msg.message?!=?WM_QUIT?)?????????????????? ???????{?? ??????????????staticFLOAT?fLastTime??=(float)::timeGetTime();?? ??????????????staticFLOAT?fCurrTime??=(float)::timeGetTime();?? ??????????????staticFLOAT?fTimeDelta?=?0.0f;?? ??????????????fCurrTime??=?(float)::timeGetTime();?? ??????????????fTimeDelta=?(fCurrTime?-?fLastTime)?/?1000.0f;?? ??????????????fLastTime??=?fCurrTime;?? ??? ??????????????if(PeekMessage(?&msg,?0,?0,?0,?PM_REMOVE?)?)?? ??????????????{?? ?????????????????????TranslateMessage(&msg?);?????????? ?????????????????????DispatchMessage(&msg?);?????????? ??????????????}?? ??????????????else?? ??????????????{?? ?????????????????????Direct3D_Update(hwnd,fTimeDelta);????????? ?????????????????????Direct3D_Render(hwnd,fTimeDelta);????????????????? ??????????????}?? ???????}<span?style="font-family:?'Microsoft?YaHei';?">????</span>?? ?
?
嗯,既然類都寫完了,用起來就是這么簡單。
?
?
六、詳細注釋的源代碼欣賞 ?
本篇文章配套的源代碼在之前的基礎上又增加了兩個文件,也就是實現雪花飛揚粒子系統類的源文件和頭文件。全部文件數量增加到了12個,它們的列表如下:
?
?
我們依舊只貼出核心代碼main.cpp,其他的眾多文件大家下源代碼回去看就好了。
?
[cpp] ?view plaincopy print?
?? ?? #define?SCREEN_WIDTH????932?????????????????????//為窗口寬度定義的宏,以方便在此處修改窗口寬度?? #define?SCREEN_HEIGHT???700?????????????????????????//為窗口高度定義的宏,以方便在此處修改窗口高度?? #define?WINDOW_TITLE????_T("【Visual?C++】游戲開發筆記系列配套示例程序五十??淺墨DirectX教程十八??雪花飛揚:實現唯美的粒子系統")?//為窗口標題定義的宏?? ?? ?? ?? #include?<d3d9.h>?? #include?<d3dx9.h>?? #include?<tchar.h>?? #include?<time.h>??? #include?"DirectInputClass.h"?? #include?"CameraClass.h"?? #include?"TerrainClass.h"?? #include?"SkyBoxClass.h"?? #include?"SnowParticleClass.h"?? ?? ?? #pragma?comment(lib,"d3d9.lib")?? #pragma?comment(lib,"d3dx9.lib")?? #pragma?comment(lib,?"dinput8.lib")?????//?使用DirectInput必須包含的庫文件,注意這里有8?? #pragma?comment(lib,"dxguid.lib")?? #pragma?comment(lib,?"winmm.lib")??? ?? ?? ?? LPDIRECT3DDEVICE9???????????????????g_pd3dDevice?=?NULL;???????????????? LPD3DXFONT??????????????????????????????g_pTextFPS?=NULL;???? LPD3DXFONT??????????????????????????????g_pTextAdaperName?=?NULL;?? LPD3DXFONT??????????????????????????????g_pTextHelper?=?NULL;?? LPD3DXFONT??????????????????????????????g_pTextInfor=?NULL;?? float???????????????????????????????????????????g_FPS=?0.0f;??????? wchar_t?????????????????????????????????????g_strFPS[50]?={0};???? wchar_t?????????????????????????????????????g_strAdapterName[60]?={0};??? D3DXMATRIX??????????????????????????g_matWorld;????????????????????????? LPD3DXMESH??????????????????????????g_pMesh?=?NULL;????????????? D3DMATERIAL9*???????????????????????g_pMaterials=?NULL;????????? LPDIRECT3DTEXTURE9*?????????????g_pTextures?=?NULL;????????? DWORD???????????????????????????????????g_dwNumMtrls?=?0;??????????? LPD3DXMESH??????????????????????????g_cylinder?=?NULL;?????????????? D3DMATERIAL9????????????????????????????g_MaterialCylinder;????????????? D3DLIGHT9???????????????????????????????g_Light;??? DInputClass*????????????????????????????????g_pDInput?=?NULL;??????????????? CameraClass*????????????????????????????g_pCamera?=?NULL;??????????????? TerrainClass*???????????????????????????????g_pTerrain?=?NULL;?????????????? SkyBoxClass*????????????????????????????????g_pSkyBox=NULL;????????????????? SnowParticleClass*??????????????????????g_pSnowParticles?=?NULL;???????? ??? ?? ?? ?? ?? LRESULT?CALLBACK????????WndProc(?HWND?hwnd,?UINT?message,?WPARAM?wParam,?LPARAM?lParam?);?? HRESULT?????????????????????Direct3D_Init(HWND?hwnd,HINSTANCE?hInstance);?? HRESULT?????????????????????Objects_Init();?? void????????????????????????????????Direct3D_Render(?HWND?hwnd,FLOAT?fTimeDelta);?? void????????????????????????????????Direct3D_Update(?HWND?hwnd,FLOAT?fTimeDelta);?? void????????????????????????????????Direct3D_CleanUp(?);?? float???????????????????????????????Get_FPS();?? void????????????????????????????????HelpText_Render(HWND?hwnd);?? ?? int?WINAPI?WinMain(HINSTANCE?hInstance,?HINSTANCE?hPrevInstance,LPSTR?lpCmdLine,?int?nShowCmd)?? {?? ?? ???? ????WNDCLASSEX?wndClass={0}?;??????????????? ????wndClass.cbSize?=?sizeof(?WNDCLASSEX?)?;???? ????wndClass.style?=?CS_HREDRAW?|?CS_VREDRAW;??? ????wndClass.lpfnWndProc?=?WndProc;????????????? ????wndClass.cbClsExtra?????=?0;?? ????wndClass.cbWndExtra?????=?0;?? ????wndClass.hInstance?=?hInstance;????????????? ????wndClass.hIcon=(HICON)::LoadImage(NULL,_T("GameMedia\\icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);? ????wndClass.hCursor?=?LoadCursor(?NULL,?IDC_ARROW?);???? ????wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);?? ????wndClass.lpszMenuName?=?NULL;??????????????????????? ????wndClass.lpszClassName?=?_T("ForTheDreamOfGameDevelop");???????? ?? ????if(?!RegisterClassEx(?&wndClass?)?)????????????? ????????return?-1;???????? ?? ????HWND?hwnd?=?CreateWindow(?_T("ForTheDreamOfGameDevelop"),WINDOW_TITLE,?????????? ????????WS_OVERLAPPEDWINDOW,?CW_USEDEFAULT,?CW_USEDEFAULT,?SCREEN_WIDTH,?? ????????SCREEN_HEIGHT,?NULL,?NULL,?hInstance,?NULL?);?? ?? ?? ???? ????if?(!(S_OK==Direct3D_Init?(hwnd,hInstance)))?? ????{?? ????????MessageBox(hwnd,?_T("Direct3D初始化失敗~!"),?_T("淺墨的消息窗口"),?0);? ????}?? ????PlaySound(L"GameMedia\\NightElf1.wav",?NULL,?SND_FILENAME?|?SND_ASYNC|SND_LOOP);??? ?? ?????? ?? ????MoveWindow(hwnd,200,10,SCREEN_WIDTH,SCREEN_HEIGHT,true);??? ????ShowWindow(?hwnd,?nShowCmd?);???? ????UpdateWindow(hwnd);?? ?? ???? ????g_pDInput?=?new?DInputClass();?? ????g_pDInput->Init(hwnd,hInstance,DISCL_FOREGROUND?|?DISCL_NONEXCLUSIVE,DISCL_FOREGROUND?|?DISCL_NONEXCLUSIVE);?? ?? ???? ????MSG?msg?=?{?0?};?? ????while(?msg.message?!=?WM_QUIT?)????????? ????{?? ????????static?FLOAT?fLastTime??=?(float)::timeGetTime();?? ????????static?FLOAT?fCurrTime??=?(float)::timeGetTime();?? ????????static?FLOAT?fTimeDelta?=?0.0f;?? ????????fCurrTime??=?(float)::timeGetTime();?? ????????fTimeDelta?=?(fCurrTime?-?fLastTime)?/?1000.0f;?? ????????fLastTime??=?fCurrTime;?? ?? ????????if(?PeekMessage(?&msg,?0,?0,?0,?PM_REMOVE?)?)??? ????????{?? ????????????TranslateMessage(?&msg?);??????? ????????????DispatchMessage(?&msg?);???????? ????????}?? ????????else?? ????????{?? ????????????Direct3D_Update(hwnd,fTimeDelta);????????? ????????????Direct3D_Render(hwnd,fTimeDelta);??????????? ????????}?? ????}?? ?? ????UnregisterClass(_T("ForTheDreamOfGameDevelop"),?wndClass.hInstance);?? ????return?0;???? }?? ?? ?? ?? LRESULT?CALLBACK?WndProc(?HWND?hwnd,?UINT?message,?WPARAM?wParam,?LPARAM?lParam?)??? {?? ????switch(?message?)??????????????? ????{?? ????case?WM_PAINT:??????????????????? ????????Direct3D_Render(hwnd,0.0f);?????????? ????????ValidateRect(hwnd,?NULL);??? ????????break;?????????????????????????????????? ?? ????case?WM_KEYDOWN:???????????????? ????????if?(wParam?==?VK_ESCAPE)???? ????????????DestroyWindow(hwnd);???? ????????break;?? ????case?WM_DESTROY:???????????????? ????????Direct3D_CleanUp();????? ????????PostQuitMessage(?0?);??????? ????????break;?????????????????????? ?? ????default:???????????????????????? ????????return?DefWindowProc(?hwnd,?message,?wParam,?lParam?);?????? ????}?? ?? ????return?0;??????????????????? }?? ?? ?? ?? HRESULT?Direct3D_Init(HWND?hwnd,HINSTANCE?hInstance)?? {?? ?? ???? ???? ???? ????LPDIRECT3D9??pD3D?=?NULL;? ????if(?NULL?==?(?pD3D?=?Direct3DCreate9(?D3D_SDK_VERSION?)?)?)? ????????????return?E_FAIL;?? ?? ???? ???? ???? ????D3DCAPS9?caps;?int?vp?=?0;?? ????if(?FAILED(?pD3D->GetDeviceCaps(?D3DADAPTER_DEFAULT,?D3DDEVTYPE_HAL,?&caps?)?)?)?? ????????{?? ????????????return?E_FAIL;?? ????????}?? ????if(?caps.DevCaps?&?D3DDEVCAPS_HWTRANSFORMANDLIGHT?)?? ????????vp?=?D3DCREATE_HARDWARE_VERTEXPROCESSING;??? ????else?? ????????vp?=?D3DCREATE_SOFTWARE_VERTEXPROCESSING;? ?? ???? ???? ???? ????D3DPRESENT_PARAMETERS?d3dpp;??? ????ZeroMemory(&d3dpp,?sizeof(d3dpp));?? ????d3dpp.BackBufferWidth????????????=?SCREEN_WIDTH;?? ????d3dpp.BackBufferHeight???????????=?SCREEN_HEIGHT;?? ????d3dpp.BackBufferFormat???????????=?D3DFMT_A8R8G8B8;?? ????d3dpp.BackBufferCount????????????=?2;?? ????d3dpp.MultiSampleType????????????=?D3DMULTISAMPLE_NONE;?? ????d3dpp.MultiSampleQuality?????????=?0;?? ????d3dpp.SwapEffect?????????????????=?D3DSWAPEFFECT_DISCARD;??? ????d3dpp.hDeviceWindow??????????????=?hwnd;?? ????d3dpp.Windowed???????????????????=?true;?? ????d3dpp.EnableAutoDepthStencil?????=?true;??? ????d3dpp.AutoDepthStencilFormat?????=?D3DFMT_D24S8;?? ????d3dpp.Flags??????????????????????=?0;?? ????d3dpp.FullScreen_RefreshRateInHz?=?0;?? ????d3dpp.PresentationInterval???????=?D3DPRESENT_INTERVAL_IMMEDIATE;?? ?? ???? ???? ???? ????if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT,?D3DDEVTYPE_HAL,??? ????????hwnd,?vp,?&d3dpp,?&g_pd3dDevice)))?? ????????return?E_FAIL;?? ?? ?? ???? ?????wchar_t?TempName[60]=L"當前顯卡型號:";??? ?????D3DADAPTER_IDENTIFIER9?Adapter;?? ?????pD3D->GetAdapterIdentifier(0,0,&Adapter); ?????int?len?=?MultiByteToWideChar(CP_ACP,0,?Adapter.Description,?-1,?NULL,?0); ?????MultiByteToWideChar(CP_ACP,?0,?Adapter.Description,?-1,?g_strAdapterName,?len); ?????wcscat_s(TempName,g_strAdapterName); ?????wcscpy_s(g_strAdapterName,TempName); ?? ????if(!(S_OK==Objects_Init()))?return?E_FAIL;?? ?? ????SAFE_RELEASE(pD3D)? ?? ?? ?? ????return?S_OK;?? }?? ?? ?? HRESULT?Objects_Init()?? {?? ???? ????D3DXCreateFont(g_pd3dDevice,?36,?0,?0,?1000,?false,?DEFAULT_CHARSET,??? ????????OUT_DEFAULT_PRECIS,?DEFAULT_QUALITY,?0,?_T("Calibri"),?&g_pTextFPS);?? ????D3DXCreateFont(g_pd3dDevice,?20,?0,?1000,?0,?false,?DEFAULT_CHARSET,??? ????????OUT_DEFAULT_PRECIS,?DEFAULT_QUALITY,?0,?L"華文中宋",?&g_pTextAdaperName);??? ????D3DXCreateFont(g_pd3dDevice,?23,?0,?1000,?0,?false,?DEFAULT_CHARSET,??? ????????OUT_DEFAULT_PRECIS,?DEFAULT_QUALITY,?0,?L"微軟雅黑",?&g_pTextHelper);??? ????D3DXCreateFont(g_pd3dDevice,?26,?0,?1000,?0,?false,?DEFAULT_CHARSET,??? ????????OUT_DEFAULT_PRECIS,?DEFAULT_QUALITY,?0,?L"黑體",?&g_pTextInfor);??? ?? ?? ?? ???? ????LPD3DXBUFFER?pAdjBuffer??=?NULL;?? ????LPD3DXBUFFER?pMtrlBuffer?=?NULL;?? ?? ????D3DXLoadMeshFromX(L"angle.X",?D3DXMESH_MANAGED,?g_pd3dDevice,??? ????????&pAdjBuffer,?&pMtrlBuffer,?NULL,?&g_dwNumMtrls,?&g_pMesh);?? ???? ????D3DXMATERIAL?*pMtrls?=?(D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();? ????g_pMaterials?=?new?D3DMATERIAL9[g_dwNumMtrls];?? ????g_pTextures??=?new?LPDIRECT3DTEXTURE9[g_dwNumMtrls];?? ????for?(DWORD?i=0;?i<g_dwNumMtrls;?i++)??? ????{?? ???????? ????????g_pMaterials[i]?=?pMtrls[i].MatD3D;?? ????????g_pMaterials[i].Ambient?=?g_pMaterials[i].Diffuse;?? ?? ???????? ????????g_pTextures[i]??=?NULL;?? ????????D3DXCreateTextureFromFileA(g_pd3dDevice,?pMtrls[i].pTextureFilename,?&g_pTextures[i]);?? ????}?? ????SAFE_RELEASE(pAdjBuffer)?? ????SAFE_RELEASE(pMtrlBuffer)?? ?? ?? ?? ???? ????D3DXCreateCylinder(g_pd3dDevice,?280.0f,?10.0f,?3000.0f,?60,?60,??&g_cylinder,?0);?? ????g_MaterialCylinder.Ambient??=?D3DXCOLOR(1.0f,?0.0f,?0.0f,?1.0f);???? ????g_MaterialCylinder.Diffuse??=?D3DXCOLOR(1.0f,?0.0f,?0.0f,?1.0f);???? ????g_MaterialCylinder.Specular?=?D3DXCOLOR(0.5f,?0.0f,?0.3f,?0.3f);???? ????g_MaterialCylinder.Emissive?=?D3DXCOLOR(0.0f,?0.0f,?0.0f,?1.0f);?? ?? ???? ????::ZeroMemory(&g_Light,?sizeof(g_Light));???? ????g_Light.Type??????????=?D3DLIGHT_DIRECTIONAL;???? ????g_Light.Ambient???????=?D3DXCOLOR(0.7f,?0.7f,?0.7f,?1.0f);???? ????g_Light.Diffuse???????=?D3DXCOLOR(1.0f,?1.0f,?1.0f,?1.0f);???? ????g_Light.Specular??????=?D3DXCOLOR(0.9f,?0.9f,?0.9f,?1.0f);???? ????g_Light.Direction?????=?D3DXVECTOR3(1.0f,?1.0f,?1.0f);???? ????g_pd3dDevice->SetLight(0,?&g_Light);???? ????g_pd3dDevice->LightEnable(0,?true);???? ????g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS,?true);???? ????g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE,?true);?? ?? ???? ????g_pCamera?=?new?CameraClass(g_pd3dDevice);?? ????g_pCamera->SetCameraPosition(&D3DXVECTOR3(0.0f,?1400.0f,?-1800.0f));?? ????g_pCamera->SetTargetPosition(&D3DXVECTOR3(0.0f,?1200.0f,?0.0f));?? ????g_pCamera->SetViewMatrix();?? ????g_pCamera->SetProjMatrix();?? ?? ???? ????g_pTerrain?=?new?TerrainClass(g_pd3dDevice);????? ????g_pTerrain->LoadTerrainFromFile(L"GameMedia\\heighmap.raw",?L"GameMedia\\terrainstone.jpg");????? ????g_pTerrain->InitTerrain(200,?200,?60.0f,?8.0f);?? ?? ???? ????g_pSkyBox?=?new?SkyBoxClass(?g_pd3dDevice?);?? ????g_pSkyBox->LoadSkyTextureFromFile(L"GameMedia\\TropicalSunnyDayFront2048.png",L"GameMedia\\TropicalSunnyDayBack2048.png",L"GameMedia\\TropicalSunnyDayRight2048.png",L"GameMedia\\TropicalSunnyDayLeft2048.png",?L"GameMedia\\TropicalSunnyDayUp2048.png"); ????g_pSkyBox->InitSkyBox(50000);?? ?? ???? ????g_pSnowParticles?=?new?SnowParticleClass(g_pd3dDevice);?? ????g_pSnowParticles->InitSnowParticle();?? ?? ?? ????return?S_OK;?? }?? ?? void????????????????Direct3D_Update(?HWND?hwnd,FLOAT?fTimeDelta)?? {?? ???? ????g_pDInput->GetInput();?? ?? ???? ????if?(g_pDInput->IsKeyDown(DIK_A))??g_pCamera->MoveAlongRightVec(-3.0f);?? ????if?(g_pDInput->IsKeyDown(DIK_D))??g_pCamera->MoveAlongRightVec(?3.0f);?? ????if?(g_pDInput->IsKeyDown(DIK_W))?g_pCamera->MoveAlongLookVec(?3.0f);?? ????if?(g_pDInput->IsKeyDown(DIK_S))??g_pCamera->MoveAlongLookVec(-3.0f);?? ????if?(g_pDInput->IsKeyDown(DIK_R))??g_pCamera->MoveAlongUpVec(?3.0f);?? ????if?(g_pDInput->IsKeyDown(DIK_F))??g_pCamera->MoveAlongUpVec(-3.0f);?? ?? ???? ????if?(g_pDInput->IsKeyDown(DIK_LEFT))??g_pCamera->RotationUpVec(-0.003f);?? ????if?(g_pDInput->IsKeyDown(DIK_RIGHT))??g_pCamera->RotationUpVec(?0.003f);?? ????if?(g_pDInput->IsKeyDown(DIK_UP))??g_pCamera->RotationRightVec(-0.003f);?? ????if?(g_pDInput->IsKeyDown(DIK_DOWN))??g_pCamera->RotationRightVec(?0.003f);?? ????if?(g_pDInput->IsKeyDown(DIK_Q))?g_pCamera->RotationLookVec(0.001f);?? ????if?(g_pDInput->IsKeyDown(DIK_E))?g_pCamera->RotationLookVec(?-0.001f);?? ?? ???? ????g_pCamera->RotationUpVec(g_pDInput->MouseDX()*?0.001f);?? ????g_pCamera->RotationRightVec(g_pDInput->MouseDY()?*?0.001f);?? ?? ???? ????static?FLOAT?fPosZ=0.0f;?? ????fPosZ?+=?g_pDInput->MouseDZ()*0.03f;?? ?? ???? ????D3DXMATRIX?matView;?? ????g_pCamera->CalculateViewMatrix(&matView);?? ????g_pd3dDevice->SetTransform(D3DTS_VIEW,?&matView);?? ?? ???? ????D3DXMatrixTranslation(&g_matWorld,?0.0f,?0.0f,?fPosZ);?? ?? ?? ?? ???? ????POINT?lt,rb;?? ????RECT?rect;?? ????GetClientRect(hwnd,&rect);?? ???? ????lt.x?=?rect.left;?? ????lt.y?=?rect.top;?? ???? ????rb.x?=?rect.right;?? ????rb.y?=?rect.bottom;?? ???? ????ClientToScreen(hwnd,<);?? ????ClientToScreen(hwnd,&rb);?? ???? ????rect.left?=?lt.x;?? ????rect.top?=?lt.y;?? ????rect.right?=?rb.x;?? ????rect.bottom?=?rb.y;?? ???? ????ClipCursor(&rect);?? ?? ????ShowCursor(false);?????? ?? }?? ?? ?? ?? ?? void?Direct3D_Render(HWND?hwnd,FLOAT?fTimeDelta)?? {?? ???? ???? ???? ????g_pd3dDevice->Clear(0,?NULL,?D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,?D3DCOLOR_XRGB(100,?255,?255),?1.0f,?0);?? ?? ???? ???? ???? ????g_pd3dDevice->BeginScene();????????????????????? ?????? ???? ???? ???? ?? ?? ?? ???? ????D3DXMATRIX?mScal,mRot2,mTrans,mFinal;??? ????D3DXMatrixTranslation(&mTrans,50.0f,1200.0f,0.0f);?? ????D3DXMatrixScaling(&mScal,3.0f,3.0f,3.0f);?? ????mFinal=mScal*mTrans*g_matWorld;?? ????g_pd3dDevice->SetTransform(D3DTS_WORLD,?&mFinal); ???? ????for?(DWORD?i?=?0;?i?<?g_dwNumMtrls;?i++)?? ????{?? ????????g_pd3dDevice->SetMaterial(&g_pMaterials[i]);?? ????????g_pd3dDevice->SetTexture(0,?g_pTextures[i]); ????????g_pMesh->DrawSubset(i);?? ????}?? ?? ?? ???? ????D3DXMATRIX?TransMatrix,?RotMatrix,?FinalMatrix;?? ????D3DXMatrixRotationX(&RotMatrix,?-D3DX_PI?*?0.5f);?? ????g_pd3dDevice->SetLight(0,?&g_Light);???? ????g_pd3dDevice->SetMaterial(&g_MaterialCylinder);?? ????g_pd3dDevice->SetTexture(0,?NULL); ????for(int?i?=?0;?i?<?4;?i++)?? ????{?? ????????D3DXMatrixTranslation(&TransMatrix,?-300.0f,?0.0f,?-350.0f?+?(i?*?500.0f));?? ????????FinalMatrix?=?RotMatrix?*?TransMatrix?;?? ????????g_pd3dDevice->SetTransform(D3DTS_WORLD,?&FinalMatrix);?? ????????g_cylinder->DrawSubset(0);?? ?? ????????D3DXMatrixTranslation(&TransMatrix,?300.0f,?0.0f,?-350.0f?+?(i?*?500.0f));?? ????????FinalMatrix?=?RotMatrix?*?TransMatrix?;?? ????????g_pd3dDevice->SetTransform(D3DTS_WORLD,?&FinalMatrix);?? ????????g_cylinder->DrawSubset(0);?? ????}?? ?? ?? ???? ????g_pTerrain->RenderTerrain(&g_matWorld,?false);?? ?? ???? ????D3DXMATRIX?matSky,matTransSky,matRotSky;?? ????D3DXMatrixTranslation(&matTransSky,0.0f,-12000.0f,0.0f);?? ????D3DXMatrixRotationY(&matRotSky,?-0.000005f*timeGetTime());??? ????matSky=matTransSky*matRotSky;?? ????g_pSkyBox->RenderSkyBox(&matSky,?false);?? ?? ???? ????g_pSnowParticles->UpdateSnowParticle(fTimeDelta);?? ????g_pSnowParticles->RenderSnowParticle();?? ?? ???? ????HelpText_Render(hwnd);?? ?? ?? ???? ???? ???? ????g_pd3dDevice->EndScene();??????????????????????? ???? ???? ???? ????g_pd3dDevice->Present(NULL,?NULL,?NULL,?NULL);?? ??????? }?? ?? ?? void?HelpText_Render(HWND?hwnd)?? {?? ???? ????RECT?formatRect;?? ????GetClientRect(hwnd,?&formatRect);?? ?? ???? ????formatRect.top?=?5;?? ????int?charCount?=?swprintf_s(g_strFPS,?20,?_T("FPS:%0.3f"),?Get_FPS()?);?? ????g_pTextFPS->DrawText(NULL,?g_strFPS,?charCount?,?&formatRect,?DT_TOP?|?DT_RIGHT,?D3DCOLOR_RGBA(0,239,136,255));?? ?? ???? ????g_pTextAdaperName->DrawText(NULL,g_strAdapterName,?-1,?&formatRect,??? ????????DT_TOP?|?DT_LEFT,?D3DXCOLOR(1.0f,?0.5f,?0.0f,?1.0f));?? ?? ???? ????formatRect.left?=?0,formatRect.top?=?380;?? ????g_pTextInfor->DrawText(NULL,?L"控制說明:",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(235,123,230,255));?? ????formatRect.top?+=?35;?? ????g_pTextHelper->DrawText(NULL,?L"????W:向前飛翔?????S:向后飛翔?",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? ????formatRect.top?+=?25;?? ????g_pTextHelper->DrawText(NULL,?L"????A:向左飛翔?????D:向右飛翔",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? ????formatRect.top?+=?25;?? ????g_pTextHelper->DrawText(NULL,?L"????R:垂直向上飛翔?????F:垂直向下飛翔",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? ????formatRect.top?+=?25;?? ????g_pTextHelper->DrawText(NULL,?L"????Q:向左傾斜???????E:向右傾斜",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? ????formatRect.top?+=?25;?? ????g_pTextHelper->DrawText(NULL,?L"????上、下、左、右方向鍵、鼠標移動:視角變化?",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? ????formatRect.top?+=?25;?? ????g_pTextHelper->DrawText(NULL,?L"?????鼠標滾輪:人物模型Y軸方向移動",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? ????formatRect.top?+=?25;?? ????g_pTextHelper->DrawText(NULL,?L"????ESC鍵?:?退出程序",?-1,?&formatRect,??? ????????DT_SINGLELINE?|?DT_NOCLIP?|?DT_LEFT,?D3DCOLOR_RGBA(255,200,0,255));?? }?? ?? float?Get_FPS()?? {?? ?? ???? ????static?float??fps?=?0;? ????static?int????frameCount?=?0; ????static?float??currentTime?=0.0f; ????static?float??lastTime?=?0.0f; ?? ????frameCount++; ????currentTime?=?timeGetTime()*0.001f; ?? ???? ????if(currentTime?-?lastTime?>?1.0f)? ????{?? ????????fps?=?(float)frameCount?/(currentTime?-?lastTime); ????????lastTime?=?currentTime;? ????????frameCount????=?0; ????}?? ?? ????return?fps;?? }?? ?? ?? ?? void?Direct3D_CleanUp()?? {?? ?? ???? ????for?(DWORD?i?=?0;?i<g_dwNumMtrls;?i++)??? ????????SAFE_RELEASE(g_pTextures[i]);?? ????SAFE_DELETE(g_pTextures);??? ????SAFE_DELETE(g_pMaterials);??? ????SAFE_DELETE(g_pDInput);?? ????SAFE_RELEASE(g_cylinder);?? ????SAFE_RELEASE(g_pMesh);?? ????SAFE_RELEASE(g_pd3dDevice);?? ????SAFE_RELEASE(g_pTextAdaperName)?? ????SAFE_RELEASE(g_pTextHelper)?? ????SAFE_RELEASE(g_pTextInfor)?? ????SAFE_RELEASE(g_pTextFPS)?? ????SAFE_RELEASE(g_pd3dDevice)?? }?? ?
?
學到目前為止,我們用學的知識寫出來的游戲場景程序已經比較炫了,一些運行截圖如下:
?
?
我們操縱著攝像機在雪中飛行,那種雪花鋪面而來的感覺非常地舒服。
?
仰望天空,任雪花飄落到“身上”:
?
?
下面這張圖可以明顯看到雪花被我們規定了活動區域:
?
冒著顯卡被燒的危險拍了一張10萬粒子數量時的“暴雪”畫面,大家看右上角的幀數,只有可憐的8幀了。。。。
?
?
?
?
撲,一朵雪花砸臉上了。。。。
?
?
大家如果覺得這雪花太大了,沒事,這是淺墨為了表現力故意調大了一點。自己去調一下雪花頂點緩存POINTVERTEX vertices數組的大小即可。
?
我們可以發現,其實Direct3D的固定功能流水線學到目前,基礎知識也就那么多,頂點緩存,索引緩存,四大變換,紋理映射,網格,模板緩存等 等,把他們其中的幾個合理地組合在一起運用一下就是新的知識,這就衍生出了我們近期剛出的三篇文章中講到的地形,天空,粒子系統。以及還 沒講到的 公告板,拾取等等知識。
轉載于:https://www.cnblogs.com/dandansang/p/6481149.html
總結
以上是生活随笔 為你收集整理的三维粒子系统的实现 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。