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

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaV9kb3ZlbGVtb24=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaV9kb3ZlbGVtb24=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >

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

行,这是今天结束。

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

时间: 2024-11-02 15:10:53

DirectX (13) 粒子系统的相关文章

python 各模块

01 关于本书 02 代码约定 03 关于例子 04 如何联系我们 1 核心模块 11 介绍 111 内建函数和异常 112 操作系统接口模块 113 类型支持模块 114 正则表达式 115 语言支持模块 12 _ _builtin_ _ 模块 121 使用元组或字典中的参数调用函数 1211 Example 1-1 使用 apply 函数 1212 Example 1-2 使用 apply 函数传递关键字参数 1213 Example 1-3 使用 apply 函数调用基类的构造函数 122

转:Python标准库(非常经典的各种模块介绍)

Python Standard Library 翻译: Python 江湖群 10/06/07 20:10:08 编译 0.1. 关于本书 0.2. 代码约定 0.3. 关于例子 0.4. 如何联系我们 核心模块 1.1. 介绍 1.2. _ _builtin_ _ 模块 1.3. exceptions 模块 1.4. os 模块 1.5. os.path 模块 1.6. stat 模块 1.7. string 模块 1.8. re 模块 1.9. math 模块 1.10. cmath 模块

Direct-X学习笔记--三维粒子系统

哇塞,一转眼已经到了传说中的粒子系统了,学会了粒子系统就可以做出一些更好玩的效果啦!加油啦! 一.简介 粒子系统,正如其名称,由各种小的粒子构成.通常用来模拟火焰,爆炸,烟,水流,火花,落叶,雨,雪等等难以用具体的形状来描述的物体.单个的粒子非常简单,可以用多边形来表示,甚至用像素表示.但是,不要小看了这样微小的粒子,当粒子的数量级达到上千,上万,甚至十万时,表现力是及其震撼的! 下面附上两张DX自带的粒子系统的截图: 帅呆了...然而我并做不出来... 粒子系统有三个特性: 1.群体性:粒子如

DirectX 9.0c游戏开发手记之RPG编程自学日志之13: Drawing with DirectX Graphics (用DirectX图形绘图)(第6节)

        本文由哈利_蜘蛛侠原创,转载请注明出处!有问题请联系[email protected]   这一次我们继续来讲述Jim Adams老哥的RPG编程书籍第二版第二章的第6节:Alpha Blending,也就是alpha混合.这一节的内容不多,所以就一次性讲完吧! 我们先将这一节的各小节的标题列在下面,以供大家参考: 1. Enabling Alpha Blending (开启alpha混合) 2. Drawing with Alpha Blending (用alpha混合进行绘图

Directx 中HLSL高级着色器语言 脑补一下吧

HLSL初级教程 作者:trcj 目录 前言 1.HLSL入门 1.1什么是着色器 1.2什么是HLSL 1.3怎么写HLSL着色器 1.4怎么用HLSL着色器 2.顶点着色器 2.1可编程数据流模型 2.2顶点声明 2.3用顶点着色器实现渐变动画 3.像素着色器 3.1多纹理化 3.2多纹理效果的像素着色器 3.3应用程序 4.HLSL Effect(效果框架) 4.1Effect代码结构 4.2用Effect实现多纹理化效果 结语 参考资料 前言 本教程针对HLSL(High Level S

DirectX 11游戏编程学习笔记之7: 第6章Drawing in Direct3D(在Direct3D中绘制)(重点回顾+勘误)

        本文由哈利_蜘蛛侠原创,转载请注明出处!有问题欢迎联系[email protected]         注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候,会使用章节号而非页码.同样的情况适合于"龙书"第二版. 上一期的地址: DX 11游戏编程学习笔记之6 这一章应该是本书最长的一章了,可能也是最难的一章,所以大家一定要好好消化,仔细学习!这一章大致相当于"龙书"第二版的第7章和第8章,还添加了一些别的东西. 由于这一

Three.js开发指南---粒子和粒子系统(第七章)

使用粒子可以很容易的创建很多细小的物体,例如雨滴雪花等 本章主要内容: 1 使用ParticleBasicMaterial(基础粒子材质)来创建和设计粒子 2 使用ParticleSystem来创建一个粒子集合 3 使用已有的几何体来创建一个粒子系统 4 让粒子和粒子系统动起来 5 用纹理给粒子造型 6 使用ParticleCanvasMaterial在画布上为粒子造型 名称 描述 Sprite粒子 参数是material,生成的sprite可以设置position和scale等属性直接添加到场

将某个Qt4项目升级到Qt5遇到的问题(13条方法)

本文转载自http://hi.baidu.com/xchinux/item/9044d8ce986accbb0d0a7b87 一.将某个QT4项目改成QT5遇到的问题 该Qt4项目以前是使用Qt4.7.4 MSVC2008开发的,因为使用到了OWC10(Office Web Components),使用MSVC编译器的话无法正常升级到Qt4.8.x和Qt5,于是将编译器转成了MinGW4.7,Qt升级到了4.8.4.今天Qt 5.0.1-MinGW预编译版本发布了,于是就拿它练手,将它升级到Qt

[Shader]NGUI与粒子系统

版权所有,转载须注明出处!喜欢火影.喜欢Java.喜欢unity3D.喜欢游戏开发的都可以加入木叶村Q群:379076227 1.前言在很久很久以前,流传着在NGUI中使用unity3D自带粒子系统的方法.有一种称为RenderQ,另一种称为Render Texture.然而,或许是随着历史的原因,有些已经逐渐被人们淡忘.使用方法随着应用的条件不同或残缺不全,或效果错误.我,不相信命运,踏上了找寻遗失秘法的旅途.最后,终于在某一天,遇见了... 2.遇见故友友:来来,刚好见到你,问问你,NGUi