DirectX中的粒子系统

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DY_1024/article/details/87279030

自然界中有现象包含了大量的行为相同的,微小粒子,比如:雪花,烟火等,粒子系统通常用来描述这些场景。

粒子是自然界中一种微小的东西,所以显示例子的最好的方法就是使用点图元,但是点图元在被光栅化的时候会被映射为一个像素,此时我们就无法去灵活的操作他,因为我们要去经常的改变他的大小,或者去给这个图元映射纹理。很早之前有一个技术叫做:广告牌,广告牌就是一个四边形,通过控制他的世界矩阵,来使其总面向摄影机。

但是后来引进了一个新的点图元:点精灵。点精灵可以改变大小以及进行纹理的映射,但是不同于广告牌的是,点精灵只需要一个点,而广告牌需要4个点,这样一来就节省了大量的运算时间。

我们使用如下的顶点结构来描述粒子的位置和颜色:

struct Particle
{
    D3DXVECTOR3_position; 
    D3DCOLOR _color; 
    static const DWORD FVF;
}
const DWORD Particle::FVF=D3DFVF_XYZ | D3DEVF_DIFFUSE;

该结构仅储存了粒子的位置和颜色,也可以根据程序为结构里面添加纹理坐标。

有时候我们需要在Particle里面添加一个浮点类型的变量,来控制点精灵的尺寸的变换,然后为了反映该变化,我们需要在灵活顶点格式里面添加D3DFVF_PSIZE标记,但是大多数的图形卡都不支持这种方式进行点精灵的尺寸控制,我们可以通过绘制状态来控制点精灵的尺寸。

下面是一个顶点结构中含有尺寸成员的例子:

struct Particle
{
 D3DXVECTOR3_position; 
 D3DCOLOR_color; 
 float  _size; 
 static const DWORD FVF; 
}
const DWORD Particle::FVF=D3DEVF_XY2 | D3DFVF_DIFFUSE | D3DFVF_PSIZE;

注意:即使硬件不支持:D3DFVF_PSIZE标记,我们还是可以通过像素着色器来控制粒子的大小。

点精灵的绘制状态

点精灵的行为很大程度上是由绘制状态控制的。绘制状态有以下:

D3DRS_POINTSPRITEENABLE

一个布尔值,默认值是false

若指定为true,则规定整个当前纹理被映射到点精灵上。
若指定为false,则规定点精灵(如果其顶点结构中含纹理坐标的话)的纹理坐标所指定的纹理元应被映射到点精灵上。

D3DRS_POINTSCALEENABLE

一个布尔值,默认值为false

若指定为true,则规定点的尺寸将用观察坐标系的单位来度量。观察坐标系的单位是仅用来描述摄像机坐标系中的3D点。点精灵的尺寸将依据近大远小的原则进行相应的比例变换。
若指定为false,则规定点的尺寸将用屏幕坐标系的单位(即像素)来度量。如果您将该绘制状态指定为false,而且您想将点精灵的尺寸设为3,则点精灵将变为屏幕上一个3×3的像素区域。

device->setRenderstate(D3DRS_POINTSCALEENABLE,true):

D3DRS_POINTSIZE

用于指定点精灵的尺寸。该值可被解释为观察坐标系中的点精灵尺寸,也可被解释为屏幕坐标系中的点精灵尺寸,这主要取决于绘制状态D3DRS_POINTSCALEENABLE的设置。

下面的代码将点的尺寸设为2.5个单位。

device->setRenderstate(D3DRS_PoINTSIZE,d3d::FtoDw(2.5f));

注意:函数将第二个参数转换为DWORD类型,因为setRenderstate要求第二个参数的类型是DWORD

D3DRS_POINTSIZE_MIN

指定点精灵可取的最小尺寸。下面的例子将点的尺寸的最小值设为0.2。

device->SetRenderstate(D3DRS_POINTSI2E_MIN,d3d::FtoDw(0.2f));

D3DRS_POINTSIZE-MAX

指定点精灵可取的最大尺寸。下面的例子将点的尺寸的最大值设为了5.0:

device->setRenderState(D3DRS_POINTSIZE_MIN,d3d::FtoDw(5.0f));

·D3DRS_POINTSCALE_A,D3DRS_POINTSCALE_B,D3DRS_POINTSCALE_C

这3个常量控制了点精灵的尺寸如何随距离发生变化,这里的距离是指点精灵到摄像机的距离。给定距离和这些常量时,Direct3D使用如下公式计算点精灵的最终尺寸:

FinalSize:计算出来的最终的点精灵的尺寸

ViewportHeight:视口高度

Size:取决于绘制状态D3DRS_POINT_SIZE的值

A\B\C:分别就是D3DRS_POINTSCALE_A,D3DRS_POINTSCALE_B,D3DRS_POINTSCALE_C的值

D:观察坐标系中点精灵到摄影机的位置,因为观察坐标系中,摄影机在原点,所以D 等于 x^2 + y^2 +  z^2的和然后开平方。

其中x,y,z是点精灵在观察坐标系中的位置。

粒子及其属性

一个粒子除了位置以及颜色的信息之外还有很多的属性,比如:粒子具有一定的速度,但是我们绘制的时候不需要这些属性。所以我们将绘制粒子的数据和粒子的属性分别放在两个不同的结构体中,当我们只需要绘制粒子的时候,就将粒子的颜色以及位置信息赋值到:Particle结构体中去,当我们要创建、更新、销毁粒子的时候,需要粒子的属性采取处理相关的属性.

粒子系统及其组成

刚才我们介绍的是有关粒子的信息,粒子系统是由众多的粒子组成的,粒子系统负责对这些粒子进行维护,也就是:创建、更新、删除 、显示等操作。

虽然每一个例子系统都具有不同的行为 ,但是我们还是可以将一些通用的行为,归纳起来。然后形成同一个类——PSystem类,该类将成为所有具体粒子系统的父类。

class PSystem 
{
public: 
PSystem();
 virtual~PSystem(); 
 virtual bool init(IDirect3pDevice9* device, char* texFileName); 
 virtual void reset();
//sometimes we don't want to free the memory of a dead particle,
//but rather respawn it instead.
 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 removeDeadParticles();
protected: 
 IDirect 3DDevice9* _device; 
 D3DXVECTOR3        _origin; 
 d3d::BoundingBox   _boundingBox;
 float              _emitRate;// rate new particles are added to system 
 float              size;// size of particles
 IDirect3DTexture9* _tex; 
 IDirect3DVertexBuffer9* _vb; 
 std::list<Attribute>            _particles; 
 int    maxparticles;//max allowed particles system can have
//
//Following three data elements used for rendering the p-system efficiently
//
 DWORD vbSize;//size of vb 
 DWORD vbOffset;// offset in vb to lock
 DWORD VbBatchSize;// number of vertices to lock starting at _vboffset
}

下面挑拣一些成员变量解释:

_origin:粒子的系统源,所有的粒子都是从这个系统源里面产生,

_boundingBox:如果想限制粒子的活动范围,比如,我现在想让粒子只在山峰的某一个区域内活动,我们就可以使用外接体,然后我们定义包含了该体积的外接体,接着只需将那些超出外接体的粒子杀死就可以了。

_emitRate:粒子的增加率,单位是:粒子数/秒

_size:所有粒子的尺寸大小

_particles:粒子系统中粒子的属性列表,我们可以利用该列表在绘制粒子的时候,将列表中的某些节点复制到顶点缓存中,然后对粒子进行绘制。

_maxparticles:特定的时间里面,系统允许的最大的粒子数目,因为当增长速度大于销毁速度的时候,会有大量的粒子产生,该变量就可以帮助我们预防这种情况。

_vbsize:在一个给定时间内,顶点缓存所能储存的顶点个数。

下面是一些成员函数的解释:

PSystem/~PSystem:构造函数和析构函数,构造函数主要完成成员变量的初始化,析构函数主要完成设备接口(顶点,纹理,缓存的释放)

init:该方法完成一些Direct3D的设备的初始化工作,例如:创建顶点缓存,以储存点精灵。

创建顶点缓存的时候我们通常使用的是动态缓存,因为我们需要在每帧中对点精灵进行更新和删除,也就是需要对顶点缓存不断的进行操作,静态缓存的访问速度会比较慢,所以使用的是静态缓存。

reset:重新设定粒子系统中粒子所具有的的属性

resetParticle:该方法将重新设置某一个粒子的属性,粒子的属性的设置依赖于粒子的系统的具体细节,所以应该将这个函数生命为纯虚函数,强制子类必须实现。

addParticle:该方法为粒子系统新增加一个粒子,使用该方法将粒子加入到粒子系统之前,应该使用resetParticle将粒子初始化。

updata:该方法将对粒子系统中的所有粒子进行更新,由于更新的时候必须要将粒子的属性进行更新,粒子的属性和具体的粒子系统有关所以该方法是一个纯虚函数,强制子类实现。

render:该方法将显示系统中的所有粒子

preRender:在绘制之前,我们要做一些准备工作,比如设置绘制的状态,但是绘制状态和具体的粒子系统有关,所以该方法还是纯虚函数,强制子类实现。

postRender:用于储存一个特定的粒子系统可能已经设置好的任何的绘制状态,由于这个也依赖于具体的粒子系统,所以为纯虚函数。

isEmpty:如果当前的粒子系统中没有粒子,返回true,反之返回false。

idDead:如果当前粒子系统中的粒子都已经死亡,返回true,反之返回false。

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

Tip:该函数经常在updata中使用,用来移除已经死亡的粒子,但是对于某一些粒子系统来说,回收那些已经死亡的粒子会有更多的优势,粒子死亡或者消失的时候,我们不是重新为他分配内存,而是重新设置那些死亡粒子的属性以及状态,使其变成新的粒子,这样更具有效率一点。

猜你喜欢

转载自blog.csdn.net/DY_1024/article/details/87279030
今日推荐