新建網(wǎng)頁 1
14.2粒子系統(tǒng)的組成
粒子系統(tǒng)是粒子的集合,用來保存和顯示這些粒子。粒子系統(tǒng)維護所有粒子的全部屬性,影響系統(tǒng)中的所有粒子:粒子的尺寸,起始的位置及應(yīng)用在粒子上的紋理等。粒子系統(tǒng)的方法負責(zé)更新、顯示、殺死和創(chuàng)建粒子。
雖然不同的具體(與抽象是相對的)粒子系統(tǒng)有不同的行為,我們歸納并找到一些所有的粒子系統(tǒng)共有的基本屬性,我們把這些公共的屬性放到一個抽象的cParticleSystem基類,它是我們所有的具體粒子系統(tǒng)的父類,現(xiàn)在讓我們看一下cParticleSystem類:
class cParticleSystem
{
protected:
IDirect3DDevice9* m_device;
D3DXVECTOR3 m_origin;
cBoundingBox m_bounding_box;
float m_emit_rate; // rate new particles are added to system
float m_size; // size of particles
IDirect3DTexture9* m_texture;
IDirect3DVertexBuffer9* m_vertex_buffer;
list<sParticleAttribute> m_particles;
int m_max_particles; // max allowed particles system can have
// following three data elements used for rendering the particle system efficiently
DWORD m_vb_num; // particle number in vertex buffer
DWORD m_vb_offset; // offset in vertex buffer to lock
DWORD m_vb_batch_num; // number of vertices to lock starting at m_vb_offset
public:
cParticleSystem();
virtual ~cParticleSystem();
virtual bool init(IDirect3DDevice9* device, const char* texture_filename);
virtual void reset();
// sometimes we don't want to free the memory of a dead particle, but rather respawn it instead.
virtual void reset_particle(sParticleAttribute* particl_attr) = 0;
virtual void add_particle();
virtual void update(float time_delta) = 0;
virtual void pre_render();
virtual void render();
virtual void post_render();
bool is_empty();
bool is_dead();
protected:
virtual void remove_dead_particles();
};
一些數(shù)據(jù)成員:
· m_origin—粒子系統(tǒng)的原點, 這是粒子系統(tǒng)產(chǎn)生時的位置。
· m_bounding_box—創(chuàng)建粒子系統(tǒng)使用的邊界盒,用于限制粒子的活動范圍。例如,假如我們讓雪系統(tǒng)只落在一個圍繞高山的峰頂?shù)捏w積內(nèi),我們會定義一個包括這個體積的邊界盒, 出界的粒子將會被殺死。
· m_emit_rate—新增加到系統(tǒng)中的粒子的速度。通常的標準是每秒。
· m_size—系統(tǒng)中所有粒子的尺寸。
· m_particles—系統(tǒng)中粒子屬性的一個列表。 我們用這個列表創(chuàng)建,釋放及更新粒子。 當我們準備畫粒子時, 我們COPY列表節(jié)點的一部分到頂點緩存并畫粒子,同時我們COPY另外一批粒子,然后重復(fù)這一過程直到繪制完所有粒子。
· m_max_particles—在給定的時間內(nèi),系統(tǒng)中允許的粒子最大數(shù)。例如,如果創(chuàng)建粒子的速度比釋放快的話, 隨著時間的增長粒子的數(shù)量將會是巨大的,這個成員將避免出現(xiàn)這樣的問題。
· m_vb_num—在給定的時間內(nèi)頂點緩存中能夠保存的粒子的數(shù)量,這個值與實際的粒子系統(tǒng)中的粒子數(shù)量無關(guān)。
注意:m_vb_offset和m_vb_batch_num數(shù)據(jù)成員在渲染粒子系統(tǒng)時使用,我們在稍后討論。
方法:
cParticleSystem/ ~cParticleSystem—用來初始化默認值和用來釋放設(shè)備接口 (vertex buffer, texture)。
cParticleSystem::cParticleSystem()
{
m_device = NULL;
m_vertex_buffer = NULL;
m_texture = NULL;
}
cParticleSystem::~cParticleSystem()
{
safe_release<IDirect3DVertexBuffer9*>(m_vertex_buffer);
safe_release<IDirect3DTexture9*>(m_texture);
}
init—這個方法做與設(shè)備無關(guān)的初始化工作,比如創(chuàng)建用來保存點精靈的頂點緩存或創(chuàng)建紋理。
bool cParticleSystem::init(IDirect3DDevice9* device, const char* texture_filename)
{
// Vertex buffer's number does not equal the number of particles in our system.
// We use the vertex buffer to draw a portion of our particles at a time.
// The arbitrary number we choose for the vertex buffer is specified by the m_vb_num variable.
m_device = device;
HRESULT hr;
hr = device->CreateVertexBuffer(
m_vb_num * sizeof(sParticle),
D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,
PARTICLE_FVF,
D3DPOOL_DEFAULT, // D3DPOOL_MANAGED can't be used with D3DUSAGE_DYNAMIC
&m_vertex_buffer,
NULL);
if(FAILED(hr))
{
MessageBox(NULL, "CreateVertexBuffer() - FAILED", "ParticleSystem", MB_OK);
return false;
}
hr = D3DXCreateTextureFromFile(device, texture_filename, &m_texture);
if(FAILED(hr))
{
MessageBox(NULL, "D3DXCreateTextureFromFile() - FAILED", "ParticleSystem", MB_OK);
return false;
}
return true;
}
o 注意: 我們使用動態(tài)的頂點緩存(D3DUSAGE DYNAMIC)。 因為我們需要在每幀中更新我們的粒子,意思是我們將會去存取頂點緩存的內(nèi)存,回想一下,訪問一個靜態(tài)的頂點緩存慢得不可接受, 所以我們使用動態(tài)的頂點緩存。
o 查看我們用過的 D3DUSAGE_POINTS標記,它說明頂點緩存將保存點精靈。
o 頂點緩存的尺寸是由m_vb_num預(yù)先確定的,而且與系統(tǒng)中粒子的數(shù)量無關(guān)。 也就是說, m_vb_num將小于等于系統(tǒng)中粒子的數(shù)量。 這是因為渲染粒子系統(tǒng)是一批一批的,不是一次渲染全部。
o 我們使用默認的內(nèi)存池(pool)代替通常使用的托管內(nèi)存池,因為動態(tài)頂點緩存不能用在托管內(nèi)存池中。
reset—這個方法重新設(shè)置系統(tǒng)中每個粒子的屬性:
void cParticleSystem::reset()
{
for(list<sParticleAttribute>::iterator iter = m_particles.begin(); iter != m_particles.end(); iter++)
reset_particle(&(*iter));
}
reset_particle—這個方法重新設(shè)置粒子的屬性。如何重設(shè)粒子的屬性,這依賴于具體粒子系統(tǒng)的特性。因此我們定義這個方法為虛擬的,等待子類去實現(xiàn)。
add_particle—這個方法用來在系統(tǒng)中增加一個粒子。在增加它到粒子列表之前,使用reset_particle方法先初始化粒子:
void cParticleSystem::add_particle()
{
sParticleAttribute attr;
reset_particle(&attr);
m_particles.push_back(attr);
}
update—這個方法更新系統(tǒng)中所有的粒子。因為這個的方法的執(zhí)行取決于具體粒子系統(tǒng)的特性,因此我們定義這個方法為抽象的,等待子類去實現(xiàn)。
render—這個方法用來顯示系統(tǒng)中所有的粒子。
pre_render—用它來初始化渲染狀態(tài),在渲染前設(shè)置。 因為系統(tǒng)與系統(tǒng)之間是不同的,所以我們定義它為虛擬的。 默認將執(zhí)行下列代碼:
void cParticleSystem::pre_render()
{
m_device->SetRenderState(D3DRS_LIGHTING, FALSE);
m_device->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
m_device->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);
m_device->SetRenderState(D3DRS_POINTSIZE, float_to_dword(m_size));
m_device->SetRenderState(D3DRS_POINTSIZE_MIN, float_to_dword(0.0f));
// control the size of the particle relative to distance
m_device->SetRenderState(D3DRS_POINTSCALE_A, float_to_dword(0.0f));
m_device->SetRenderState(D3DRS_POINTSCALE_B, float_to_dword(0.0f));
m_device->SetRenderState(D3DRS_POINTSCALE_C, float_to_dword(1.0f));
// use alpha from texture
m_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}
注意:我們使用alpha混合渲染,以便設(shè)置紋理的alpha通道,來設(shè)置紋理像素的透明,用它產(chǎn)生多種效果。一種特殊的情況是:獲得象紋理那樣的非矩形的粒子。例如,獲得一個圓形“雪球形”的粒子,我們使用一個簡單的帶有alpha通道的紋理,它看上去是背景為黑色的帶有白色圓形的樣子。因此,顯示出來時只是一個白圓,這比白色的矩形紋理要好。
post_render—用它去保存所有渲染狀態(tài)。因為系統(tǒng)與系統(tǒng)間是不同的,所以我們定義它為虛擬的。默認將執(zhí)行下列代碼:
void cParticleSystem::post_render()
{
m_device->SetRenderState(D3DRS_LIGHTING, TRUE);
m_device->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE);
m_device->SetRenderState(D3DRS_POINTSCALEENABLE, FALSE);
m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
}
is_empty—如果為True則在當前的系統(tǒng)中沒有粒子, 否則為false.
bool cParticleSystem::is_empty()
{
return m_particles.empty();
}
is_dead—如果為True則系統(tǒng)中的所有粒子都是死的,否則為false。
bool cParticleSystem::is_dead()
{
for(list<sParticleAttribute>::iterator iter = m_particles.begin(); iter != m_particles.end(); iter++)
{
// Is there at least one living particle? If yes, the system is not dead.
if(iter->is_alive)
return false;
}
// No living particles found, the system must be dead.
return true;
}
remove_dead_particles—搜索屬_particle性表,從表中殺死并刪除粒子。
void cParticleSystem::remove_dead_particles()
{
list<sParticleAttribute>::iterator iter = m_particles.begin();
while(iter != m_particles.end())
{
if(! iter->is_alive)
// erase returns the next iterator, so no need to increment to the next one ourseleves.
iter = m_particles.erase(iter);
else
iter++; // next in list
}
}