笔者: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" alt="">

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

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

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

行,这是今天结束。

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

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

  1. DirectX基础 常用函数语句

    DirectX常用函数语句 常用数学类函数: 计算向量的长度(模): FLOAT D3DXVec3Length(CONST D3DXVECTOR3* pV); 向量的规范化: D3DXVECTOR3 ...

  2. directX学习系列8 颜色融合(转)

    1, Multipass(多通道)    将一个任务划分成几个阶段,由多个pass处理不同阶段,后续pass总是处理前一个pass的结果.例如复杂的光照方程可以分成几个pass来计算.    用不同的 ...

  3. DirectX 总结和DirectX 9.0 学习笔记

    转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html DirectX 总结 DDS DirectXDraw Surfac ...

  4. Unity3D深入浅出 - 新版粒子系统 (Shuriken) - Tonge

    Shuriken粒子系统是继Unity3.5版本之后推出的新版粒子系统,它采用了模块化管理,个性化的粒子模块配合粒子曲线编辑器使用户更容易创作出各种兵分复杂的粒子效果. 创建一个粒子系统的方式有两种: ...

  5. NeHe OpenGL教程 第十九课:粒子系统

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. 纠结和郁闷的存在感-关于DirectX与HLSL的矩阵存储方式---转载好文章

    我常常这么大胆的认为,搞科学的人总是喜欢用各种让常人难以理解的复杂方式去处理某些其实可能很简单的事情,这种情况在他自身擅长的.有着诸多对手竞争的领域上极为常见.比如吧,搞DirectX的人用了左手坐标 ...

  7. DirectX 3D 之C#开发

    C#下进行directX的3D开发,一个旋转的4棱锥的例子. 建议看两个文档<Managed DirectX 9图形和游戏编程简略中文文档>和<Managed DirectX 9 S ...

  8. [原]Unity3D深入浅出 - 新版粒子系统 (Shuriken)

    Shuriken粒子系统是继Unity3.5版本之后推出的新版粒子系统,它采用了模块化管理,个性化的粒子模块配合粒子曲线编辑器使用户更容易创作出各种兵分复杂的粒子效果. 创建一个粒子系统的方式有两种: ...

  9. Visual C#使用DirectX实现视频播放

    Visual C#使用DirectX实现视频播放 visual|视频播放 - 很多人第一次接触到DirectX大都是通过游戏,至于安装.升级DirectX的原因无非是满足游戏运行的需要.Direct ...

随机推荐

  1. mongodb.conf

    # mongodb.conf # Where to store the data. dbpath=/var/lib/mongodb #where to log logpath=/var/log/mon ...

  2. rm-rf 恢复过程中滥用

    多DBA一定rm -rf讨厌它,也许有一天自己将数据库,以消除一个中午,然后.那么就没有一个--这种情况下,--这个不幸真的发生,你真的无药可救?不必要,有解决方法.也许你遇到不幸时,有一天.你可以用 ...

  3. 终结者:具体解释Nginx(一)

            相信非常多人都听过Nginx.这个小巧的东西能够和Apache及IIS相媲美. 那么它有什么作用呢?一句话.它是一个减轻Web应用server(如Tomcat)压力和实现Web应用se ...

  4. Docker安装应用程序(Centos6.5_x64)

    Docker安装应用程序(Centos6.5_x64) Authoer::jom_ch@2014/7/23 Docker官方网站 http://www.docker.com/ 一,安装EPEL 关于E ...

  5. BCP导出导入

    BCP导出导入大容量数据实践   前言 SQL SERVER提供多种不同的数据导出导入的工具,也可以编写SQL脚本,使用存储过程,生成所需的数据文件,甚至可以生成包含SQL语句和数据的脚本文件.各有优 ...

  6. SharePoint 2013 禁用搜索服务

    原文:SharePoint 2013 禁用搜索服务 前言,在SharePoint2013中,对于硬件需求的提升,让我们虚机里安装总是一筹莫展,尤其开启了搜索服务以后,对于内存的消耗就更加严重,尤其对于 ...

  7. Android定调的发展

    首先,介绍一下Android系统支持的铃声格式. 有下面几种: 64赫兹Midi,AAC.AAC+.AMR.WAV.MP3.Real Audio.WMA.OGG等格式. 将音频文件设置成铃声非常eas ...

  8. Chromium on Android: Android在系统Chromium为了实现主消息循环分析

    总结:刚开始接触一个Chromium on Android时间.很好奇Chromium主消息循环是如何整合Android应用. 为Android计划,一旦启动,主线程将具有Java消息层循环处理系统事 ...

  9. 【git学习五】git基础之git分支

    1.背景                最早用github的时候,我傻傻的问舍友大神,git里面的branch是干什么的,他用了非常直白的解释,我至今还记得."branch就是你能够自己建立 ...

  10. NSOJ 畅通工程(并查集)

    某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可). ...