DirectX (13) 粒子系统

    xiaoxiao2021-12-14  16

    目录(?)[+]

    作者:i_dovelemon

    来源:

    日期:2014 / 10 / 16

    主题:Point Sprite, Particle System

    引言

                 在游戏中,很多的绚丽,灿烂的特效,都可以使用粒子系统制作出来。那么粒子系统,到底是什么?它是如何工作的?接下来的文章,将会向大家讲述如何构建一个简单的粒子系统。

    粒子系统

                所谓的粒子系统,指的是一个粒子管理器,用于在特定条件下产生特定的粒子,并且赋予粒子以运动。所以,可以将粒子系统看成是一个个粒子的集合。而不同的效果,是要我们自己来控制粒子如何产生,粒子的颜色如何变化,粒子如何进行运动。通过控制这些条件,我们就能够创造出很多很多的特效出来。而关键就在于如何控制粒子的运动和产生。

               既然,我们明白了粒子系统的概念,那么一个粒子到底是什么?它在计算机中是如何进行表示的?

               简单而言,粒子在计算机中就是使用一个结构来表达。结构中保存了粒子的基本属性,如位置,颜色,生命,以及运动状态相关的参数。不同复杂度的粒子系统,粒子所包含的属性并不相同。如果需要简单的效果,那么只需要几个基本的属性即可。如果要做出更加复杂,或者更加符合动力学的粒子系统,可以在结构中再添加很多不同的物理属性。至于如何添加这些属性,要依赖于你所需要实现的粒子系统的复杂程度,想要支持的功能来进行设计。

               当我们为粒子系统设计好了一个粒子结构之后,并且也有了粒子系统,来负责粒子的产生,以及运动等等。我们需要的当然就是显示在界面上。如果没有显示任何的内容,即使你的系统再强大,也是白费力气。

               DirectX中支持很多的基本单元类型,最常用的如三角形列表,线条列表等等。在这里,我们将会使用一个称之为Point Sprite的基本单元类型。

               Point Sprite,实际上就是一个点。我们在应用程序阶段,只需要将它当做点进行处理即可。但是要显示效果,我们自然还是需要进行纹理映射。由于Point Sprite的特殊性,DirectX内部会自动的为这些点设置纹理坐标。注意,这里的点只是逻辑意义上的。DirectX在最后处理的时候,还是会使用多边形来进行处理。所以这里说的点,存在一个大小的问题。我们能够通过程序来控制产生的点的大小。

               为了实现一些效果,我们需要开启Alpha blend。毕竟做粒子特效,如果没有进行颜色混合的话,就是一个一个的单独的纹理,这并不是粒子效果了。

                而且,粒子在界面显示的时候的先后顺序,对于我们来说并不重要。所以,将depth test以及depth buffer禁用掉,能够提高系统的效率。

    基本类的设计

              在上面,说了这么多,最终要体现在代码上面,下面是粒子系统的抽象类。当我们需要创建一个新的效果的时候,只要继承这个类,并且复写虚函数即可。当然,这里的只是一个很简单的粒子系统设计,提供的粒子属性也很少。但是也能够做出很多的效果出来了。如果读者,希望更复杂的效果,就可以自己来扩展这个基本类别,然后添加你自己的功能。

              废话不多说,直接上代码:

    [cpp]  view plain  copy   //-----------------------------------------------------------------------   // declaration  : Copyright (c), by XJ , 2014. All right reserved .   // brief        : This file will define the Particle system   // author       : XJ   // file         : PSystem.h   // date         : 2014 / 10 / 15   // version      : 1.0   //-----------------------------------------------------------------------   #pragma once      #include<d3dx9.h>   #include"AABB.h"   #include"Camera.h"   #include<vector>   using namespace XJCollision ;   using namespace std ;      struct Particle   {       D3DXVECTOR3 initPos ;       D3DXVECTOR3 initVelocity;       float       initSize    ;       //in pixel       float       initTime    ;       float       lifeTime    ;       float       mass        ;       D3DXCOLOR   initColor   ;          static IDirect3DVertexDeclaration9* decl ;   };      class PSystem   {   public:       PSystem(           const char* fxName,           const char* techName,           const char* texName,           const D3DXVECTOR3& accel,           const AABB& box,           int maxNumParticles,           float timePerParticle,           LPDIRECT3DDEVICE9 device,           Camera* camera);       virtual ~PSystem();      public:       float getTime();       void setTime(float fTime);       const AABB& getAABB() const ;       void setWorldMtx(const D3DXMATRIX& world);       void addParticle();          virtual void onLostDevice();       virtual void onResetDevice();          virtual void initParticles(Particle& out) = 0;       virtual void update(float dt);       virtual void draw() ;      protected:       LPDIRECT3DDEVICE9 m_pDevice;                // Device       ID3DXEffect     *m_FX   ;                   // Effect       D3DXHANDLE       m_hTech;                   // Technique       D3DXHANDLE       m_hWVP ;                   // WVP matrix       D3DXHANDLE       m_hEyePosL;                //        D3DXHANDLE       m_hTex;                    // Texture       D3DXHANDLE       m_hTime;                   // Time       D3DXHANDLE       m_hAccel;                  // Accel       D3DXHANDLE       m_hViewportHeight;         // Viewport's height              IDirect3DTexture9* m_Tex;                   // Texture       IDirect3DVertexBuffer9* m_VB    ;           // Vertex buffer       D3DXMATRIX       m_World;                   // World matrix       D3DXMATRIX       m_InvWorld;                // Inverse matrix       float            m_Time ;                   // Time       D3DXVECTOR3      m_Accel ;                  // Accelerate       AABB             m_AABB;                    // Bounding box       int              m_MaxNumParticles;         // Max number particles       float            m_fTimePerParticle;        // Delay time to emit one particle          Camera          *m_pCamera  ;               // Camera          std::vector<Particle> m_Particles;        // Particles list       std::vector<Particle*>  m_AliveParticles; // Alive particles list       std::vector<Particle*>    m_DeadParticles;    // Dead particles list   };// end for PSystem  

    [cpp]  view plain  copy   #include"PSystem.h"   #include"MyUtil.h"   #include<d3dx9.h>      IDirect3DVertexDeclaration9* Particle::decl = NULL ;   /**  * Constructor  */   PSystem::PSystem(const char* fxName,           const char* techName,           const char* texName,           const D3DXVECTOR3& accel,           const AABB& box,           int maxNumParticles,           float timePerParticle,           LPDIRECT3DDEVICE9 device,           Camera* camera)   {       //Save the device       m_pDevice = device ;          //Create error buffer       ID3DXBuffer* _error = NULL ;       HR(D3DXCreateBuffer(128, &_error));          //Create the Effect       HR(D3DXCreateEffectFromFileA(m_pDevice, fxName,           NULL,NULL, D3DXSHADER_DEBUG, NULL, &m_FX, &_error));          //If error        if(_error)       {           MessageBoxA(NULL,(char*)_error->GetBufferPointer(),"Error", MB_OK);           return ;       }          //Get the technique handle       m_hTech = m_FX->GetTechniqueByName(techName);          //Get all the handle       m_hWVP = m_FX->GetParameterByName(0, "gWVP");       m_hEyePosL = m_FX->GetParameterByName(0, "gEyePosL");       m_hTex = m_FX->GetParameterByName(0, "gTex");       m_hTime = m_FX->GetParameterByName(0, "gTime");       m_hAccel = m_FX->GetParameterByName(0, "gAccel");       m_hViewportHeight = m_FX->GetParameterByName(0, "gViewportHeight");          //Create the texture       HR(D3DXCreateTextureFromFileA(m_pDevice, texName, &m_Tex));          //Set parameters       HR(m_FX->SetTechnique(m_hTech));       HR(m_FX->SetTexture(m_hTex, m_Tex));       HR(m_FX->SetVector(m_hAccel, &D3DXVECTOR4(m_Accel,0.0f)));          //Save the time per particles       m_fTimePerParticle = timePerParticle ;          m_Time = 0.0f ;          //Save the AABB       m_AABB = box ;          //Save the camera       m_pCamera = camera ;          //Allocate the memory for the particle       m_MaxNumParticles = maxNumParticles ;       m_Particles.resize(m_MaxNumParticles);       m_AliveParticles.reserve(m_MaxNumParticles);       m_DeadParticles.reserve(m_MaxNumParticles);          //They start all dead       for(int i = 0 ; i < m_MaxNumParticles ; i ++)       {           m_Particles[i].initTime = 0.0f ;           m_Particles[i].lifeTime = -1.0f ;       }          //Create the vertex buffer       HR(m_pDevice->CreateVertexBuffer(m_MaxNumParticles * sizeof(Particle), D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY|           D3DUSAGE_POINTS,0,D3DPOOL_DEFAULT,&m_VB,NULL));   }// end for constructor      /**  * Destructor  */   PSystem::~PSystem()   {       m_AliveParticles.clear();       m_DeadParticles.clear();       m_Particles.clear();       m_FX->Release();       m_Tex->Release();       m_VB->Release();   }// end for destructor      void PSystem::setTime(float fTime)   {       m_Time = fTime ;   }// end for setTime      void PSystem::setWorldMtx(const D3DXMATRIX& world)   {       m_World = world ;       D3DXMatrixInverse(&m_World,NULL,&m_World);   }// end for setWorldMtx      void PSystem::addParticle()   {       if(m_DeadParticles.size() > 0)       {           Particle* p = m_DeadParticles.back();           initParticles(*p);              m_DeadParticles.pop_back();           m_AliveParticles.push_back(p);       }   }// end for addParticle      void PSystem::onLostDevice()   {       //OnlostDevice for fx       m_FX->OnLostDevice();          if(m_VB)       {           m_VB->Release();           m_VB = NULL ;       }   }// end for onLostDevice      void PSystem::onResetDevice()   {       //OnResetDevice for fx       m_FX->OnResetDevice();          if(m_VB)       {           //Recreate the vertex buffer           HR(m_pDevice->CreateVertexBuffer(m_MaxNumParticles*sizeof(Particle),               D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY|D3DUSAGE_POINTS,0,               D3DPOOL_DEFAULT,               &m_VB,               NULL));       }   }// end for onResetDevice      void PSystem::update(float dt)   {       m_Time += dt ;          //Rebuild the dead list and alive list       m_DeadParticles.resize(0);       m_AliveParticles.resize(0);          //For each particle       for(int i = 0 ; i < m_Particles.size() ; i ++)       {           if((m_Time - m_Particles[i].initTime) > m_Particles[i].lifeTime)           {               m_DeadParticles.push_back(&m_Particles[i]);           }           else           {               m_AliveParticles.push_back(&m_Particles[i]);           }       }// end for           //Check if it is the time to emit one another particle       if(m_fTimePerParticle > 0.0f)       {           static float timeDelay = 0.0f ;           timeDelay += dt ;              if(timeDelay > m_fTimePerParticle)           {               addParticle();               timeDelay = 0.0f ;           }       }   }// end for update      void PSystem::draw()   {       //Get the camera's position in the world and make it relative to the particle system's local system       D3DXVECTOR3 eyeW = m_pCamera->pos();       D3DXVECTOR3 eyeL ;       D3DXVec3TransformCoord(&eyeL, &eyeW, &m_InvWorld);          //Set the FX parameter       HR(m_FX->SetValue(m_hEyePosL,&eyeL,sizeof(D3DXVECTOR3)));       HR(m_FX->SetFloat(m_hTime, m_Time));       HR(m_FX->SetMatrix(m_hWVP, &(m_World* m_pCamera->viewproj())));       HR(m_FX->SetInt(m_hViewportHeight, 600));          //Draw       //set the vertex buffer       HR(m_pDevice->SetStreamSource(0, m_VB, 0, sizeof(Particle)));          //set the vertex declaration       HR(m_pDevice->SetVertexDeclaration(Particle::decl));          Particle* p = 0 ;       HR(m_VB->Lock(0, 0, (void**)&p, D3DLOCK_DISCARD));       UINT vIndex = 0 ;       for(int i = 0 ; i < m_AliveParticles.size(); i ++)       {           p[vIndex] = *m_AliveParticles[i] ;           vIndex ++ ;       }// end for          HR(m_VB->Unlock());          UINT numPass = 0 ;       HR(m_FX->Begin(&numPass, 0));       HR(m_FX->BeginPass(0));          if(vIndex > 0)       {           m_pDevice->DrawPrimitive(D3DPT_POINTLIST,0,vIndex);       }          HR(m_FX->EndPass());       HR(m_FX->End());   }// end for draw                  我在这个类的基础上继承了一个类,用于实现自己的效果:

    [cpp]  view plain  copy   #pragma once      #include"PSystem.h"      class FireRing: public PSystem   {   public:       FireRing(const char* fxName,           const char* techName,           const char* texName,           const D3DXVECTOR3& accel,           const AABB& box,           int maxNumParticles,           float timePerParticle,           LPDIRECT3DDEVICE9 device,           Camera* camera)           :PSystem(fxName, techName, texName, accel,           box, maxNumParticles, timePerParticle, device, camera)       {          };          void initParticles(Particle& out)       {           //Save the init time           out.initTime = m_Time ;              //Calculate the life time from 2.0s to 4.0s           out.lifeTime = 20.0f + 2 * (rand()001 * 0.0001) ;              //Calculate the initialize size in pixel           out.initSize = 50.0f + 10 * (rand()001 * 0.0001) ;              //Calculate the a very small velocity           static float angle = 0.0f ;           D3DXVECTOR3 vel(0.5f, 1.0f, 0.5f);           D3DXMATRIX m ;              D3DXMatrixRotationY(&m,angle);           D3DXVec3TransformCoord(&vel, &vel, &m);              out.initVelocity = vel ;           D3DXVec3Normalize(&out.initVelocity, &out.initVelocity);           angle += 1.0f ;              //Calculate the mass           out.mass = 1.0f + (rand()001 * 0.0001) ;              //Calculate the color           float t = (0.5f + 0.5*(rand()001 * 0.0001)) ;           out.initColor = D3DXCOLOR(0.0f, 0.0f, t * 1.0f, t * 1.0f);              //Calculate the pos           out.initPos.x = 0.0f;           out.initPos.y = 0.0f ;           out.initPos.z = 0.0f ;          }// end for initParticle   };                 这个类只要复写它的粒子初始化函数就可以了。通过在初始化的里面进行设计,改变粒子的位置,状态等等,我们还是能够做出很多的效果出来。下面是这个效果配套的 Shader

    [cpp]  view plain  copy   uniform extern float4x4 gWVP ;   uniform extern texture gTex ;   uniform extern float3  gEyePosL;   uniform extern float3  gAccel ;   uniform extern float   gTime ;   uniform extern float   gViewportHeight ;      sampler TexS = sampler_state   {       Texture = <gTex>;       MinFilter = LINEAR ;       MagFilter = LINEAR ;       MipFilter = POINT  ;       AddressU = CLAMP ;       AddressV = CLAMP ;   };      struct OutputVS   {       float4 posH : POSITION ;       float4 color: COLOR0   ;       float2 tex0 : TEXCOORD0 ;       float  size : PSIZE ;   };      //VS   OutputVS FireRingVS(           float3 posL: POSITION,           float3 vel : TEXCOORD0,           float size : TEXCOORD1,           float time : TEXCOORD2,           float lifeTime: TEXCOORD3,           float mass : TEXCOORD4,           float4 color: COLOR0   )   {       //Clear the output       OutputVS outVS = (OutputVS)0 ;          float t = gTime - time ;          posL = posL + t * vel ;          outVS.posH = mul(float4(posL,1.0f), gWVP);          size += 0.8 * t ;          float d = distance(posL, gEyePosL);       outVS.size = size ; //gViewportHeight * size / (1.0 + 8.0f*d);       color.r = 0.0f ;       color.g = 0.0f ;       color.b = 1.0f * (t / lifeTime) ;        color.a = 1.0f - 1.0f * (t / lifeTime) ;        outVS.color = color ;//(1.0f - (t / lifeTime)) ;          return outVS ;   }      //PS   float4 FireRingPS(float4 color:COLOR0,                     float2 tex0: TEXCOORD0):COLOR   {       return color * tex2D(TexS, tex0);   }      technique FireRingTech   {       pass P0       {           vertexShader = compile vs_2_0 FireRingVS();           pixelShader = compile ps_2_0 FireRingPS();              PointSpriteEnable = true ;           AlphaBlendEnable = true ;           SrcBlend = One ;           DestBlend = One ;           ZWriteEnable = false ;       }   }                   这个粒子的效果如下截图:

                  稍微改下,还能实现这样的效果:

                      

                    粒子系统的关键就在与如何控制粒子的产生,运动等等。通过控制这些,你能够实现任何你想要的效果。

                    好了,今天到这里结束了。

    转载请注明原文地址: https://ju.6miu.com/read-965673.html

    最新回复(0)