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类派生出几个具体的粒子系统。为了突出一些关键技术,这些系统设计得比较简单,并没有用到这个类的全部功能。我们分别实现了雪,焰火和粒子枪系统。这些系统名称清楚地表明了所要模拟的对象。雪系统模拟的是飘落的雪花。焰火系统模拟的是类似焰火的爆炸过程。粒子枪系统的粒子源位于摄像机的位置,出射(按下键盘中的某一个键后)方向向沿着摄像机的观察方向,这看起来好像是在发射“粒子炮弹”,可作为游戏中枪炮系列的基础。

 

       

      

原创粉丝点击