DirectX 3D_基础之粒子系统的组成 绘制粒子系统 粒子随机性 具体的粒子系统
来源:互联网 发布:百乐官方淘宝授权店 编辑:程序博客网 时间:2024/05/01 22:36
每日一语:
自己一直想改变自己的性格,一些性格上面的弱点,但一直没有改变过来,从小到大,太计较别人的想法,而忽视了自己内心的声音。一直感觉是在为别人在活,为别人活,本无可厚非,为家人,为朋友。但很少考虑自己,所以自己毫无自己的个性可言。我应该改变,学会倾听自己内心的声音。每个人的道路,都是自己走出来的,我应该,我行我素,让自己的个性发扬。做软件的必须有自己的看法,想法,这样,伟大的想法,或者产品才会有可能在自己的身上出现。
正文:
粒子系统的组成:
粒子系统是众多粒子的集合,并负责对这些粒子进行维护和显示。粒子系列跟踪系统中影响所有粒子状态的全局属性,例如粒子的尺寸,粒子的粒子源,将要映射到粒子的纹理等。按照功能来说,粒子系统主要负责更新,显示,杀死以及创建粒子。
虽然不同的粒子系统具有不同的行为,我们仍可归纳并找到一些所有粒子系统都需要的基本属性。我们将这些通用属性封装在一个抽象基类--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类派生出几个具体的粒子系统。为了突出一些关键技术,这些系统设计得比较简单,并没有用到这个类的全部功能。我们分别实现了雪,焰火和粒子枪系统。这些系统名称清楚地表明了所要模拟的对象。雪系统模拟的是飘落的雪花。焰火系统模拟的是类似焰火的爆炸过程。粒子枪系统的粒子源位于摄像机的位置,出射(按下键盘中的某一个键后)方向向沿着摄像机的观察方向,这看起来好像是在发射“粒子炮弹”,可作为游戏中枪炮系列的基础。
- DirectX 3D_基础之粒子系统的组成 绘制粒子系统 粒子随机性 具体的粒子系统
- DirectX 3D_基础之粒子系统 广告牌技术 粒子和点精灵 点精灵的结构 点精灵的绘制状态 粒子及其属性
- DirectX 3D_实践之DirectX3D中粒子系统的使用
- DirectX (13) 粒子系统
- DirectX (13) 粒子系统
- 简单的粒子系统~~
- 粒子系统的管理
- 粒子系统的学习
- iOS的粒子系统
- 粒子系统的实现
- Unity Notes之控制粒子系统的最大粒子数量
- 粒子系统
- 粒子系统
- 粒子系统
- 粒子系统
- 粒子系统
- 粒子系统
- 粒子系统。
- poj 1005 I Think I Need a Houseboat
- python之import机制
- IOS第四天——Obj-C的数据类型和表达式
- oracl 10g 完全删除
- Dojo 学习笔记--TabContainer
- DirectX 3D_基础之粒子系统的组成 绘制粒子系统 粒子随机性 具体的粒子系统
- Unity3d之standalone PC游戏崩溃
- Fedora14安装Nvidia驱动并实现3D桌面效果
- NOIP 合唱队形 (LIS应用)
- 项目二-成员函数、友元函数和一般函数之区别
- Win7+Cygwin-20070321+ns-all-inone-2.34
- 基础一
- Linux学习笔记——轻松学Linux shell编程 第一部分:Linux初学者编程入门
- linux模块管理命令