您的位置:首页 > 其它

DirectX 3D_基础之粒子系统的组成 绘制粒子系统 粒子随机性 具体的粒子系统

2013-04-16 22:37 423 查看
每日一语:

自己一直想改变自己的性格,一些性格上面的弱点,但一直没有改变过来,从小到大,太计较别人的想法,而忽视了自己内心的声音。一直感觉是在为别人在活,为别人活,本无可厚非,为家人,为朋友。但很少考虑自己,所以自己毫无自己的个性可言。我应该改变,学会倾听自己内心的声音。每个人的道路,都是自己走出来的,我应该,我行我素,让自己的个性发扬。做软件的必须有自己的看法,想法,这样,伟大的想法,或者产品才会有可能在自己的身上出现。

正文:


粒子系统的组成:


粒子系统是众多粒子的集合,并负责对这些粒子进行维护和显示。粒子系列跟踪系统中影响所有粒子状态的全局属性,例如粒子的尺寸,粒子的粒子源,将要映射到粒子的纹理等。按照功能来说,粒子系统主要负责更新,显示,杀死以及创建粒子。

虽然不同的粒子系统具有不同的行为,我们仍可归纳并找到一些所有粒子系统都需要的基本属性。我们将这些通用属性封装在一个抽象基类--PSystem类中,该类将作为我们所要实现的全部具体的粒子系统的父类。让我们来研究一下该类的特点和功能。

class PSystem

{

public:

PSystem();

virtual ~PSystem();

virtual bool init(IDirect3DDevice9 * device,char* texFileName);

virtual void reset();

virtual void resetParticle(Attribute* attribute) = 0;

virtual void addParticle();

virtual void update(float timeDelta) = 0;

virtual void preRender();

virtual void render();

virtual void postRender();

bool isEmpty();

bool isDead();

protected:

virtual void removeDeadParticle();

protected:

IDirect3DDevice9* _device;

D3DXVECTOR3 _origin;

d3d::BoundingBox _boundingBox;

float _emitRate;

float _size;

IDirect3DTexture9* _tex;

IDirect3DVertexBuffer9* _vb;

std::list<Attribute> _particles;

int _maxParticles;

DWORD _vbSize;

DWORD _vbOffset;

DWORD _vbBatchSize;

};

我们选出一部分成员来加以说明:

_origin 系统粒子源,所有的粒子都将从系统粒子源产生。

_boundingBox 如果项限制粒子的活动范围,可使用外接体。例如:假定在一个雪粒子系统中,我们想让雪只落在山峰周围的某个空间体积内时,我们就可定义包含了该体积的外接体,然后只需将那些跃出该外接体的粒子杀死就可以了。

_emitRate 系统中新粒子的增加率,该值用粒子数/秒来度量。

_size 系统中所有粒子的尺寸。

_particle 系统中粒子的属性列表。我们可利用该列表对粒子进行创建,销毁和更新。准备绘制粒子时,我们将该列表中的一部分结点复制到顶点缓存中,然后对粒子进行绘制。然后再复制和绘制另外一批粒子,重复该过程,直至所有粒子绘制完毕。当然,这种绘制方法将问题过分简化了。

_maxParticles 在某个给定时间,系统所允许拥有的最大粒子数。例如,如果粒子的创建速度大于被销毁的速度,随时间的增长,将会有海量的粒子产生。该成员变量可帮助我们避免那种情况的发生。

_vbSize 在一个给定时间顶点缓存中所存储的顶点个数。该值不依赖粒子系统中的实际粒子个数。

注意:数据成员_vbOffset和_vbBatchSize都可用于粒子系统的绘制。

下面是一些成员函数:

PSystem/~PSystem 该构造函数将成员变量初始化为默认值,析构函数完成设备接口(顶点缓存,纹理)的释放。

init 该方法完成一些Direct3D设备相关的初始化工作,例如创建顶点缓存以存储以存储点精灵,创建纹理等。顶点缓存的创建过程中包含了一些标记,我们在先前的讨论中尚未提及。

hr = device -> CreateVertexBuffer(

_vbSize * sizeof(Particle),

D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,

Particle:FVF,

D3DPOL_DEFAULT,

&_vb,

0

);

注意,这里我们使用了动态缓存。这是因为我们需要在每帧中对粒子进行更新,这就意味着我们需要访问顶点缓存的存储区。前面我们提到静态缓存的访问速度相当慢,所以,我们将顶点缓存指定为动态的。

注意,我们使用了标记D3DUSAGE_POINTS,该标记指定了顶点缓存将用于存储点精灵。

注意,顶点缓存的尺寸已由变量_vbSize预先定义好,该值与系统中的粒子个数无关。即,_vbSize基本上不可能与系统中的粒子数相等。这是因为我们绘制粒子系统时往往是分批绘制的,而非一蹴而就。

我们使用了默认的内存池而非常用的托管内存池,这是因为动态顶点缓存不允许被放置在托管内存池中。

reset 该方法重新设定系统中每个粒子的属性

void PSystem::reset()

{

std::list<Attribute>::iterator i;

for(i = _particles.begin();i != _particles.end();i++)

{

resetParticle(&(*i));

}

}

resetParticle 该方法重新设定粒子的属性值。粒子的属性应如何重新设置依赖于特定粒子系统的细节。所以,我们将该方法声明为抽象函数,并强制子类必须实现该函数。

addParticle 该方法为系统增加一个粒子。该方法在将粒子加入列表之前,首先调用resetParticle方法对粒子进行初始化。

void PSystem::addParticle()

{

Attribute attribute;

resetParticle(&attribute);

_particles.push_back(attribute);

}

update 该方法用于对系统中的所有粒子进行更新。由于该方法的实现依赖于特定粒子系统的细节,我们将该方法声明为抽象方法,并强制子类必须实现该方法。

render 该方法用于显示系统中的所有粒子。该方法的实现相当复杂。

preRender 该方法用于在绘制之前,对那些必须设置的初始化绘制状态进行设置。由于该方法的实现也依赖于具体的系统。我们将其声明为虚函数。该方法的默认实现如下:

void PSystem::preRender()

{

_device->SetRenderState(D3DRS_LIGHTING,false);

_device->SetRenderState(D3DRS_POINTSPRITEENABLE,true);

_device->SetRenderState(D3DRS_POINTSCALEENABLE,true);

_device->SetRenderState(D3DRS_POINTSIZE,d3d::FtoDw(_size));

_device->SetRenderState(D3DRS_POINTSIZE_MIN,d3d::FtoDw(0.0f));

// control the size of particle relatie to distance

_device->SetRenderState(D3DRS_POINTSCALE_A,d3d::FtoDw(0.0f));

_device->SetRenderState(D3DRS_POINTSCALE_B,d3d::FtoDw(0.0f)):

_device->SetRenderState(D3DRS_POINTSCALE_C,d3d::FtoDw(1.0f));

// use alpha from texture

_device->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);

_device->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);

_device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);

_device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);

_device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);

}

注意,我们启用了Alpha融合,这样当前纹理的Alpha通道就指定了纹理像素的透明度。我们以此来产生各种效果。一种常用的场合是获取那些像素纹理一样的非矩形的粒子。例如,要想获取一个圆形的像雪球一样的粒子,我们可使用一个具有Alpha通道(黑色背景中有一个白色圆)的纯白色纹理。这样,只有白色的圆形会显示出来,矩形的白色纹理就得不到显示。

postRender 用于存储一个特定粒子系统可能已设置好的任何绘制状态。由于这也依赖于具体的系统,我们将该函数声明为虚函数。其默认实现如下:

void PSystem::postRender()

{

_device->SetRenderState(D3DRS_LIGHTING,true);

_device->SetRenderState(D3DRS_POINTSPRITEENABLE,false);

_device->SetRenderState(D3DRS_POINTSCALEENABLE,false);

_device->SetRenderState(D3DRS_ALPHABLENDENABLE,false);

}

isEmpty 如果当前系统中没有粒子,该函数返回true,反之返回false.

isDead 如果系统中的所有粒子均已死亡,该函数返回true,反之返回false.

removeDeadParticles 对属性列表_Particle进行搜索,并从列表中移除任何已死亡的粒子。

void PSystem::removeDeadParticles();

{

std::list<Attribute>::itertor i;

i = _particles.begin():

while(i != _particles.end() )

{

if( i ->_isAlive == false)

{

i = _particles.erae(i);

}

else

{

i++;

}

}

}

该方法通常在子类的update方法中被调用,以移除任何已被杀死(标记为已死亡)的粒子,但是对于某些粒子系统,回收那些处于死亡状态的粒子比销毁它们有更多的优势。在粒子诞生和消亡时,我们不是从列表中为其重新分配内存或回收内存,我们只是对那些处于死亡状态的粒子重新设置,使其变为新粒子。

绘制一个粒子系统:

由于粒子系统是动态的,我们需要在每帧中更新系统中的粒子。一直直观但是缺乏效率的粒子系统绘制方法是:创建一个足够容纳最大数目个粒子的顶点缓存。

对于每一帧进行的操作如下:

1,更新所有的粒子。

2,将所有处于活动状态的粒子复制到顶点缓存中。

3,绘制顶点缓存中的粒子。

该方法是可行的,但是效率不高,图形卡的效率不高,一种更好的方法是,创建一个容量合理的顶点缓存。然后我们将该顶点缓存划分为若干片段。

对每一帧的操作如下:

1,更新所有粒子。

2,将全部活动粒子被绘制。

3,若顶点缓存未满,则用标记D3DLOCK_NOOVERWRITE锁定片段i。将500个粒子复制到锁定片段i中。

4,若顶点缓存已满,则用自顶点缓存的起始位置开始i = 0。将用标记D3DLOCK_DISCARD锁定片段i。将500个粒子复制到锁定片段i中。绘制片段i中的粒子。下一片段,i++。

注意,前面我们将顶点缓存指定为动态的,所以,我们在锁定顶点缓存时,使用了动态缓存标记D3DLOCK_NOOVERWRITE和D3DLOCK_DISCARD.这些标记允许我们对顶点缓存中未被绘制的部分进行锁定,这样做丝毫不影响顶点缓存中其余部分的绘制。例如,假定我们要绘制片段0中的粒子,通过使用标记D3DLOCK_NOOVERWRITE,当我们绘制片段0中的粒子时,可对片段1锁定并填充。这就避免了可能出现的绘制中断情况。

关于绘制方案的实现,我们需要在代码中,详细了解。

下面只介绍几个成员变量:

_vbSize 在某一给定时间点,顶点缓存中所存储的粒子数。该值不依赖于实际粒子系统中的顶点数。

_vbOffset 该变量为自顶点缓存首地址算起的偏移量,标记了下一批粒子将重顶点缓存的何处开始复制。例如,如果第一批粒子存储在顶点缓存的0到499项,则下一批粒子在顶点缓存中开始复制的偏移量将为500.

_vbBatchSize 每批粒子的数目。


随机性:


系统中的粒子都具有某种随机性。例如,如果想模拟雪,我们不希望所有的雪花都精确按照同一种方式飘雪。为了实现粒子系统的随机功能,加入两个函数。

第一个函数返回一个位于区间[LowBound,highBound]内的随机浮点数。

float d3d::GetRandomFloat(float lowBound,float highBound)

{

if( lowBound >= highBound)

{

return lowBound;

}

float f = (rand() % 1000) * 0.0001f;

return (f * (highBound - lowBound)) + lowBound;

}

第二个函数输出一个被限制在由最小点min和最大点max确定的外接体中的随机向量。

void d3d::GetRandomVector(

D3DXVECTOR3* out,

D3DXVECTOR3* min,

D3DXVECTOR3* max)

{

out -> x = GetRandomFloat(min->x,max->x);

out -> y = GetRandomFloat(min->y,max->y);

out -> z = GetRandomFloat(min->z,max->z);

}

具体的粒子系统:

现在,由我们PSystem类派生出几个具体的粒子系统。为了突出一些关键技术,这些系统设计得比较简单,并没有用到这个类的全部功能。我们分别实现了雪,焰火和粒子枪系统。这些系统名称清楚地表明了所要模拟的对象。雪系统模拟的是飘落的雪花。焰火系统模拟的是类似焰火的爆炸过程。粒子枪系统的粒子源位于摄像机的位置,出射(按下键盘中的某一个键后)方向向沿着摄像机的观察方向,这看起来好像是在发射“粒子炮弹”,可作为游戏中枪炮系列的基础。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐