• DirectX (13) 粒子系统


    笔者:i_dovelemon

    资源:CSDN

    日期:2014 / 10 / 16

    主题:Point Sprite, Particle System



    介绍

                 在游戏中。非常多的绚丽,灿烂的特效,都能够使用粒子系统制作出来。那么粒子系统。究竟是什么?它是怎样工作的?接下来的文章,将会向大家讲述怎样构建一个简单的粒子系统。



    粒子系统

                所谓的粒子系统,指的是一个粒子管理器,用于在特定条件下产生特定的粒子。而且赋予粒子以运动。

    所以,可以将粒子系统看成是一个个粒子的集合。

    而不同的效果。是要我们自己来控制粒子怎样产生,粒子的颜色怎样变化,粒子怎样进行运动。通过控制这些条件,我们就行创造出非常多非常多的特效出来。而关键就在于怎样控制粒子的运动和产生。

               既然,我们明确了粒子系统的概念。那么一个粒子究竟是什么?它在计算机中是怎样进行表示的?

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

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

               DirectX中支持非常多的基本单元类型,最经常使用的如三角形列表,线条列表等等。

    在这里,我们将会使用一个称之为Point Sprite的基本单元类型。

               Point Sprite。实际上就是一个点。

    我们在应用程序阶段,仅仅须要将它当做点进行处理就可以。可是要显示效果,我们自然还是须要进行纹理映射。因为Point Sprite的特殊性。DirectX内部会自己主动的为这些点设置纹理坐标。注意,这里的点仅仅是逻辑意义上的。

    DirectX在最后处理的时候。还是会使用多边形来进行处理。

    所以这里说的点,存在一个大小的问题。我们可以通过程序来控制产生的点的大小。

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

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


    基本类的设计

              在上面,说了这么多,终于要体如今代码上面。以下是粒子系统的抽象类。当我们须要创建一个新的效果的时候,仅仅要继承这个类,而且复写虚函数就可以。当然,这里的仅仅是一个非常easy的粒子系统设计,提供的粒子属性也非常少。可是也可以做出非常多的效果出来了。假设读者。希望更复杂的效果,就行自己来扩展这个基本类别。然后加入你自己的功能。

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

    //-----------------------------------------------------------------------
    // 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

    #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

                   我在这个类的基础上继承了一个类,用于实现自己的效果:

    #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()%10001 * 0.0001) ;
    
    		//Calculate the initialize size in pixel
    		out.initSize = 50.0f + 10 * (rand()%10001 * 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()%10001 * 0.0001) ;
    
    		//Calculate the color
    		float t = (0.5f + 0.5*(rand()%10001 * 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:

    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 ;
    	}
    }

                    这个粒子的效果例如以下截图:


                  略微改下。还能实现这种效果:

                     

                    粒子系统的关键就在与怎样控制粒子的产生,运动等等。通过控制这些,你可以实现不论什么你想要的效果。

                    行,这是今天结束。

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    SpringBoot结合ShardingSphere实现分库分表、读写分离
    SpringBoot结合ShardingSphere实现主从读写分离
    使用Sentinel实现Spring Cloud Gateway网关流量控制
    使用Sentinel实现热点参数限流
    对比学习UIKit和AppKit--入门级
    UIViewController
    C++的异常处理之一:throw是个一无是处的东西
    About Closure
    理解Objective C 中id
    关于文件压缩的一些小知识
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4658597.html
Copyright © 2020-2023  润新知