您的位置:首页 > 其它

基于Direct3D实现简单的粒子系统

2010-03-03 17:25 489 查看
这是一个基于D3D的基本的粒子系统,能够实现一些基本的效果,如:雨、雪、烟花等。代码很少,只有一个头文件和一个CPP文件,便于研究粒子系统的原理。

EpParticleSystem.h:

#ifndef _EPPARTICLESYSTEM_H_
#define _EPPARTICLESYSTEM_H_

#include <d3d9.h>
#include <d3dx9.h>

struct EpParticle
{
D3DXVECTOR3 pos ;
float psize ;
D3DCOLORVALUE color ;
D3DXVECTOR3 velo ;
};

struct stParticleVertex
{
D3DVECTOR pos ;
float psize ;
D3DCOLOR color ;
};

#define D3DFVF_PARTICLE (D3DFVF_XYZ | D3DFVF_PSIZE | D3DFVF_DIFFUSE)

//生成随机数
inline float rangeRand (float min , float max );
//float->DWORD
inline DWORD FtoDW (float val ) { return * ((DWORD * )& val ); }

class EpParticleSystem
{
public :
EpParticleSystem (D3DVECTOR position , D3DVECTOR range , D3DVECTOR acceleration ,
D3DVECTOR emitterPositionMin , D3DVECTOR emitterPositionMax ,
D3DVECTOR velocityMin , D3DVECTOR velocityMax ,
D3DCOLORVALUE colorMin , D3DCOLORVALUE colorMax ,
float psizeMin , float psizeMax ,
int maxCount , int emitCount , float emitInterval ,
LPDIRECT3DDEVICE9 device , LPDIRECT3DTEXTURE9 texture );
virtual ~ EpParticleSystem ();
bool Initialize ();
void Update (float delta );
bool Render ();

protected :
void emit (int emitCount );

protected :
D3DXVECTOR3 m_Pos ; //位置
D3DXVECTOR3 m_Range ; //范围
D3DXVECTOR3 m_Accel ; //加速度
D3DXVECTOR3 m_EmiPosMin , m_EmiPosMax ; //发射器位置
D3DXVECTOR3 m_VeloMin , m_VeloMax ; //粒子速度
D3DCOLORVALUE m_ColorMin , m_ColorMax ; //颜色
float m_PSizeMin , m_PSizeMax ; //粒子大小
int m_Count , m_MaxCount ; //粒子数量及上限
int m_EmiCount ; float m_EmiInterval ; //发射数量,发射间隔时间
float m_Interval ; //累计时间

EpParticle * m_Particles ; //粒子池

LPDIRECT3DDEVICE9 m_pDevice ; //设备对象
LPDIRECT3DTEXTURE9 m_pTexture ; //纹理
LPDIRECT3DVERTEXBUFFER9 m_pVB ; //顶点缓冲区

D3DXMATRIX m_WorldMat ; //世界转换矩阵
};
#endif

EpParticleSystem.cpp:

#include "EpParticleSystem.h"
#include <stdlib.h>
#include <time.h>

EpParticleSystem :: EpParticleSystem ( D3DVECTOR position , D3DVECTOR range ,
D3DVECTOR acceleration , D3DVECTOR emitterPositionMin , D3DVECTOR emitterPositionMax ,
D3DVECTOR velocityMin , D3DVECTOR velocityMax , D3DCOLORVALUE colorMin ,
D3DCOLORVALUE colorMax , float psizeMin , float psizeMax ,
int maxCount , int emitCount , float emitInterval ,
LPDIRECT3DDEVICE9 device , LPDIRECT3DTEXTURE9 texture )
{
m_Pos = position ;
m_Range = range ;
m_Accel = acceleration ;
m_EmiPosMin = emitterPositionMin ;
m_EmiPosMax = emitterPositionMax ;
m_VeloMin = velocityMin ;
m_VeloMax = velocityMax ;
m_ColorMin = colorMin ;
m_ColorMax = colorMax ;
m_PSizeMin = psizeMin ;
m_PSizeMax = psizeMax ;
m_Count = 0 ;
m_MaxCount = maxCount ;
m_EmiCount = emitCount ;
m_EmiInterval = emitInterval ;
m_Interval = 0.0f ;
m_pDevice = device ;
m_pTexture = texture ;
m_Particles = NULL ;
m_pVB = NULL ;
//随机数种子
srand ( (unsigned )time ( NULL ) );
//计算世界矩阵
D3DXMatrixTranslation (& m_WorldMat , m_Pos . x , m_Pos . y , m_Pos . z );
}

bool EpParticleSystem :: Initialize ()
{
if (m_pDevice == NULL )
{
return false ;
}
//构建粒子池
m_Particles = new EpParticle [ m_MaxCount ];
if (m_Particles == NULL )
{
return false ;
}
//创建顶点缓冲区
if (FAILED (m_pDevice -> CreateVertexBuffer (sizeof (stParticleVertex ) * m_MaxCount ,
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS ,
D3DFVF_PARTICLE , D3DPOOL_DEFAULT , & m_pVB , NULL )))
return false ;

return true ;
}

void EpParticleSystem :: Update ( float delta )
{
EpParticle * p = NULL ;
for (int i = 0 ; i < m_Count ; i ++ )
{
//改变粒子的速度与位置
p = & m_Particles [ i ];
p-> pos += p-> velo ;
p-> velo += m_Accel ;

//检查粒子是否超出边界
if (! (p-> pos . x <= m_Range . x / 2 && p-> pos . x >= - m_Range . x / 2 &&
p-> pos . y <= m_Range . y / 2 && p-> pos . y >= - m_Range . y / 2 &&
p-> pos . z <= m_Range . z / 2 && p-> pos . z >= - m_Range . z / 2 ))
{
//将最后一个粒子移到该位置
m_Particles [ i ] = m_Particles [ m_Count - 1 ];
m_Count -- ; //抛弃最后一个粒子
i -- ; //重新检查该粒子
}
}
//发射粒子
m_Interval += delta ;
if (m_Interval >= m_EmiInterval )
{
emit (m_EmiCount );
m_Interval -= m_EmiInterval ;
}
}
//生成随机数
float rangeRand ( float min , float max )
{
return (float )rand () / RAND_MAX * (max - min ) + min ;
}
//发射一定数量的粒子
void EpParticleSystem :: emit ( int emitCount )
{
EpParticle * p = NULL ;
for (int i = 0 ; i < emitCount ; i ++ )
{
if (m_Count == m_MaxCount ) break ;
p = & m_Particles [ m_Count ];
p-> pos . x = rangeRand (m_EmiPosMin . x , m_EmiPosMax . x );
p-> pos . y = rangeRand (m_EmiPosMin . y , m_EmiPosMax . y );
p-> pos . z = rangeRand (m_EmiPosMin . z , m_EmiPosMax . z );
p-> velo . x = rangeRand (m_VeloMin . x , m_VeloMax . x );
p-> velo . y = rangeRand (m_VeloMin . y , m_VeloMax . y );
p-> velo . z = rangeRand (m_VeloMin . z , m_VeloMax . z );
p-> psize = rangeRand (m_PSizeMin , m_PSizeMax );
p-> color . r = rangeRand (m_ColorMin . r , m_ColorMax . r );
p-> color . g = rangeRand (m_ColorMin . g , m_ColorMax . g );
p-> color . b = rangeRand (m_ColorMin . b, m_ColorMax . b);
p-> color . a = rangeRand (m_ColorMin . a , m_ColorMax . a );
m_Count ++ ;
}
}

bool EpParticleSystem :: Render ()
{
if (m_Count == 0 )
{
return true ;
}
//将粒子从粒子池转移到顶点缓冲区
stParticleVertex * p;
if (FAILED (m_pVB -> Lock (0 , sizeof (stParticleVertex ) * m_Count , (void ** )& p, 0 )))
return false ;

for (int i = 0 ; i < m_Count ; i ++ )
{
p[ i ]. pos = m_Particles [ i ]. pos ;
p[ i ]. psize = m_Particles [ i ]. psize ;
p[ i ]. color = D3DCOLOR_COLORVALUE (m_Particles [ i ]. color . r ,
m_Particles [ i ]. color . g , m_Particles [ i ]. color . b, m_Particles [ i ]. color . a );
}

m_pVB -> Unlock ();

m_pDevice -> SetTransform (D3DTS_WORLD , & m_WorldMat );
m_pDevice -> SetTexture (0 , m_pTexture );
m_pDevice -> SetRenderState (D3DRS_LIGHTING , FALSE );
m_pDevice -> SetRenderState (D3DRS_ALPHABLENDENABLE , TRUE );
m_pDevice -> SetRenderState (D3DRS_DESTBLEND , D3DBLEND_ONE );
m_pDevice -> SetRenderState (D3DRS_POINTSPRITEENABLE , TRUE );
m_pDevice -> SetRenderState (D3DRS_POINTSCALEENABLE , TRUE );
m_pDevice -> SetRenderState (D3DRS_POINTSCALE_A , FtoDW (0.0f ));
m_pDevice -> SetRenderState (D3DRS_POINTSCALE_B , FtoDW (0.0f ));
m_pDevice -> SetRenderState (D3DRS_POINTSCALE_C , FtoDW (10.0f ));

m_pDevice -> SetFVF (D3DFVF_PARTICLE );
m_pDevice -> SetStreamSource (0 , m_pVB , 0 , sizeof (stParticleVertex ));
m_pDevice -> DrawPrimitive (D3DPT_POINTLIST , 0 , m_Count );

m_pDevice -> SetRenderState (D3DRS_LIGHTING , TRUE );
m_pDevice -> SetRenderState (D3DRS_ALPHABLENDENABLE , FALSE );
m_pDevice -> SetRenderState (D3DRS_POINTSPRITEENABLE , FALSE );

return true ;
}

EpParticleSystem ::~ EpParticleSystem ()
{
if (m_Particles != NULL )
{
delete [] m_Particles ;
m_Particles = NULL ;
}
if (m_pVB != NULL )
{
m_pVB -> Release ();
m_pVB = NULL ;
}
}

下面一个文件是使用这个粒子系统实现一个烟火效果的例子。

main.cpp

#include <d3d9.h>
#include <d3dx9.h>
#include "EpParticleSystem.h"

#define WINDOW_CLASS "MyWndCls"
#define WINDOW_NAME "Particle System Test"
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define FULLSCREEN FALSE

bool InitializeD3D (HWND hWnd , bool fullscreen );
bool InitializeObjects ();
void RenderScene ();
void Shutdown ();

LPDIRECT3D9 g_D3D = NULL ;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL ;

D3DXMATRIX g_ProjMat ; //投影矩阵
D3DXMATRIX g_ViewMat ; //视图矩阵

LPDIRECT3DTEXTURE9 g_Texture = NULL ; //用于粒子的纹理

EpParticleSystem * g_Firework = NULL ; //粒子系统

float g_LastTime = 0.0f ; //用于计算时间增量
float g_Time = 0.0f ;

LRESULT WINAPI MsgProc (HWND hWnd , UINT msg , WPARAM wParam , LPARAM lParam )
{
switch (msg )
{
case WM_DESTROY:
PostQuitMessage (0 );
return 0 ;
break ;

case WM_KEYUP:
if (wParam == VK_ESCAPE )
PostQuitMessage (0 );
break ;
}

return DefWindowProc (hWnd , msg , wParam , lParam );
}

int WINAPI WinMain (HINSTANCE hInst , HINSTANCE prevhInst , LPSTR cmdLine , int show )
{
WNDCLASSEX wc = { sizeof (WNDCLASSEX ), CS_CLASSDC , MsgProc , 0L , 0L ,
GetModuleHandle (NULL ), NULL , LoadCursor (NULL , IDC_ARROW ), NULL , NULL ,
WINDOW_CLASS , NULL };
RegisterClassEx (& wc );

HWND hWnd = CreateWindow (WINDOW_CLASS , WINDOW_NAME , WS_POPUP ,
100 , 100 , WINDOW_WIDTH , WINDOW_HEIGHT ,
NULL , NULL , wc . hInstance , NULL );

if (InitializeD3D (hWnd , FULLSCREEN ))
{
ShowWindow (hWnd , show );
UpdateWindow (hWnd );

MSG msg = { 0 };

while (msg . message != WM_QUIT )
{
if (PeekMessage (& msg , NULL , 0 U , 0 U , PM_REMOVE ))
{
TranslateMessage (& msg );
DispatchMessage (& msg );
}
else
RenderScene ();
}
}

Shutdown ();

UnregisterClass (WINDOW_CLASS , wc . hInstance );
return 0 ;
}

bool InitializeD3D (HWND hWnd , bool fullscreen )
{
D3DDISPLAYMODE displayMode ;

g_D3D = Direct3DCreate9 (D3D_SDK_VERSION );
if (g_D3D == NULL ) return false ;

if (FAILED (g_D3D -> GetAdapterDisplayMode (D3DADAPTER_DEFAULT , & displayMode )))
return false ;

D3DPRESENT_PARAMETERS d3dpp = { 0 };

if (fullscreen )
{
d3dpp . Windowed = FALSE ;
d3dpp . BackBufferWidth = WINDOW_WIDTH ;
d3dpp . BackBufferHeight = WINDOW_HEIGHT ;
}
else
d3dpp . Windowed = TRUE ;
d3dpp . SwapEffect = D3DSWAPEFFECT_DISCARD ;
d3dpp . BackBufferFormat = displayMode . Format ;

if (FAILED (g_D3D -> CreateDevice (D3DADAPTER_DEFAULT , D3DDEVTYPE_HAL , hWnd ,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE ,
& d3dpp , & g_D3DDevice ))) return false ;

if (! InitializeObjects ()) return false ;

return true ;
}

bool InitializeObjects ()
{
//加载纹理
if (D3DXCreateTextureFromFile (g_D3DDevice , "snow.tga" , & g_Texture ) != D3D_OK )
return false ;

D3DVECTOR position = D3DXVECTOR3 (0.0f , 0.0f , 0.0f ); //粒子系统的位置
D3DVECTOR range = D3DXVECTOR3 (4.0f , 4.0f , 4.0f ); //长宽高范围
D3DVECTOR accel = D3DXVECTOR3 (0.0f , - 0.0002f , 0.0f ); //加速度
D3DVECTOR emiPosMin = D3DXVECTOR3 (- 0.05f , - 2.0f , - 0.05f ); //发射位置的范围
D3DVECTOR emiPosMax = D3DXVECTOR3 (0.05f , - 2.0f , 0.05f );
D3DXVECTOR3 veloMin = D3DXVECTOR3 (- 0.006f , 0.02f , - 0.006f ); //粒子初始速度范围
D3DXVECTOR3 veloMax = D3DXVECTOR3 (0.006f , 0.03f , 0.006f );
D3DCOLORVALUE colorMin = { 0.0f , 0.0f , 0.0f , 0.0f }; //粒子颜色范围
D3DCOLORVALUE colorMax = { 1.0f , 1.0f , 1.0f , 0.0f };
float psizeMin = 0.3f ; //粒子大小范围
float psizeMax = 0.5f ;
int maxCount = 3000 ; //最大粒子数量
int emiCount = 10 ; //每次发射数量
float emiInterval = 0.01f ; //发射间隔时间
g_Firework = new EpParticleSystem (position , range , accel , emiPosMin , emiPosMax ,
veloMin , veloMax , colorMin , colorMax , psizeMin , psizeMax , maxCount , emiCount , emiInterval ,
g_D3DDevice , g_Texture );
//初始化粒子系统
g_Firework -> Initialize ();

g_D3DDevice -> SetSamplerState (0 , D3DSAMP_MINFILTER , D3DTEXF_ANISOTROPIC );
g_D3DDevice -> SetSamplerState (0 , D3DSAMP_MAGFILTER , D3DTEXF_ANISOTROPIC );

//计算投影矩阵
D3DXMatrixPerspectiveFovLH (& g_ProjMat , 45.0f , 4.0f / 3 , 0.1f , 1000.0f );
g_D3DDevice -> SetTransform (D3DTS_PROJECTION , & g_ProjMat );

//计算视图矩阵
D3DXVECTOR3 cameraPos (0.0f , 0.0f , - 3.0f );
D3DXVECTOR3 lookAtPos (0.0f , - 1.0f , 0.0f );
D3DXVECTOR3 upDir (0.0f , 1.0f , 0.0f );
D3DXMatrixLookAtLH (& g_ViewMat , & cameraPos , & lookAtPos , & upDir );
g_D3DDevice -> SetTransform (D3DTS_VIEW , & g_ViewMat );

return true ;
}

void RenderScene ()
{
//计算时间增量
g_Time = GetTickCount () * 0.001f ;
float interval = g_Time - g_LastTime ;
g_LastTime = g_Time ;

g_Firework -> Update (interval );

g_D3DDevice -> Clear (0 , NULL , D3DCLEAR_TARGET , D3DCOLOR_XRGB (0 , 0 , 0 ), 1.0f , 0 );

g_D3DDevice -> BeginScene ();

g_Firework -> Render ();

g_D3DDevice -> EndScene ();

g_D3DDevice -> Present (NULL , NULL , NULL , NULL );
}

void Shutdown ()
{
if (g_D3DDevice != NULL ) g_D3DDevice -> Release ();
g_D3DDevice = NULL ;

if (g_D3D != NULL ) g_D3D -> Release ();
g_D3D = NULL ;

if (g_Texture != NULL ) g_Texture -> Release ();
g_Texture = NULL ;

if (g_Firework != NULL ) delete g_Firework ;
g_Firework = NULL ;
}

实现的效果如下:



这个粒子系统还很原始,在创建的时候需要传入太多的参数,并且只能硬编码,今后可以增加对脚本的支持。此外还有很多局限,比如粒子系统的范围只能是一个立方体,而实际需求可能会是圆柱体等形状。当前的粒子系统创建后不能移动,也不能修改其他参数,只能使用一个加速度,粒子的颜色不能衰减,粒子不能自行湮灭,只能移动到区域外被回收,只能使用单一的纹理等,这些都是需要改进的地方。

所用的纹理下载:http://download.csdn.net/source/1656023

源代码下载:http://download.csdn.net/source/1656048



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ntwilford/archive/2009/09/12/4546207.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: