XNA 粒子系统入门
几经周折,终于做出来了一个简单的粒子系统。很简单,很丑,但我相信会对想了解这方面的人有所帮助。
什么是粒子系统
粒子系统是利用粒子模拟自然场景的一种技术,例如说雨,雪,水流,爆炸,烟雾等场景。由于这些场景都是根据物理模型计算出来的,也可以说,粒子系统是基于物理原理的一种建模方法。
粒子类
粒子系统是由粒子组成的。一般具有的特征包括质量、位置、速度、受力(能量)、生命周期等。很多个粒子也可以相互作用、组合、效果叠加。
下面是我的类:
Code
public class Particle
{
//粒子的位置
public bool isActive;
//粒子的位置
private Vector3 position;
public Vector3 Position
{
set { position = value; }
get { return position; }
}
//粒子速度
private Vector3 velocity;
public Vector3 Velocity
{
set { velocity = value; }
get { return velocity; }
}
/// <summary>
/// 实例一个粒子
/// </summary>
/// <param name="position"></param>
/// <param name="velocity"></param>
public Particle(Vector3 position,Vector3 velocity)
{
this.position = position;
this.velocity = velocity;
isActive = true;
}
}
粒子系统类
粒子系统主要是用来控制粒子的运动和绘制粒子。不清楚作用的话就看代码。
Code
public class ParticleSystem:DrawableGameComponent
{
GameMain game;
Model sphere;
Effect balleffect;
//最大粒子数
Random rnd ;
List<Particle> particles;
int maxParticlesNum = 600;
float time = 0;
public ParticleSystem(Game game):base(game)
{
this.game = (GameMain)game;
particles = new List<Particle>();
}
protected override void LoadContent()
{
sphere = Game.Content.Load<Model>("sphere");
balleffect = game.Content.Load<Effect>("balleffect");
base.LoadContent();
}
public override void Update(GameTime gameTime)
{
time += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (particles.Count < maxParticlesNum/* && (int)(time * 100) % 6 == 0*/)
{
//x、z轴方向的速度随机
rnd = new Random((int)DateTime.Now.Ticks);
float x = (float)rnd.Next(-3, 3) / 10;
float z = (float)rnd.Next(-3, 3) / 10;
//生成速度向量
Vector3 vel = new Vector3(x, 1.6f,z);
//加入一个新的粒子
particles.Add(new Particle(Vector3.Zero,vel));
}
for(int i=0;i<particles.Count;i++)
{
Particle p = particles[i];
//让粒子速度向下
p.Velocity -= new Vector3(0, 0.005f, 0);
p.Position += p.Velocity;
particles[i] = p;
if ( particles[i].Position.Y<-300)
//移除一个粒子
particles.RemoveAt(i);
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
foreach (Particle particle in particles)
{
//如果是活动粒子……
if (particle.isActive)
{
foreach (ModelMesh mesh in sphere.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = world;
effect.View = game.camera.View;
effect.Projection = game.camera.Projection;
}
mesh.Draw();
}
}
}
base.Draw(gameTime);
}
}
两个类加上游戏主框架,就可以看到效果了,如下图。
这是从两个不同方向看到粒子从两个地方发出(粒子都是一个个球啊,由于拉的太远所以看起来有点像点)
改进
不过现在这样式很不乍的(主要是一点也看不出里面是一个个的小球),下面来给它们加上环境光和颜色。
HLSL代码如下:
Code
float4x4 World;
float4x4 View;
float4x4 Projection;
float4 color;
float3 EyePos;
//方向光
float4 lightDir:Direction
<
string Object="DirectionalLight";
string Space="World";
> ={1.0f,-1.0f,1.0f,0.0f};
//漫反射光
float4 lightColor:Diffuse
<
string UINAme="Dirffuse Light Color";
string Object="DirectionalLight";
> ={1.0f,1.0f,1.0f,1.0f};
//环境光
float4 lightAmbient:Ambient
<
string UIWidget="Ambient Light Color";
string Space="material";
> ={255.0f,255.0f,255.0f,1.0f};
//镜面反射能力
float shininess:SpecularPower
<
string UIWidget="slider";
float UIMin=1.0;
float UIMax=128.0;
float UIStep=1.0;
string UIName="specular power";
> =30;
struct VertexShaderInput
{
float4 Position : POSITION0;
float3 normal:NORMAL;
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float4 diffAmbColor:COLOR0;
float4 specCol:COLOR1;
};
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;
float4x4 normalMat;
normalMat=mul(World,View);
float4 worldPosition = mul(input.Position, World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
float4 N=mul(input.normal,normalMat);
float3 E=normalize(EyePos-output.Position);
float3 L=normalize(mul(-lightDir.xyz,normalMat));
float3 H=normalize(E+L);
output.diffAmbColor=lightColor*max(0,dot(N,L))+lightAmbient;
output.specCol=lightColor*pow(max(0,dot(N,H)),shininess);
return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
return input.diffAmbColor*color+input.specCol;
}
technique Technique1
{
pass Pass1
{
VertexShader = compile vs_1_1 VertexShaderFunction();
PixelShader = compile ps_1_1 PixelShaderFunction();
}
}
当然粒子的代码也要改啊
下面是新的代码:
在粒子类中加入字段:public Color color;用于保存粒子的颜色
粒子类的构造方法中加入:
Code
byte[] byts = new byte[4];
new Random().NextBytes(byts);
color =new Color(byts[0],byts[1],byts[2],255);
随机产生一个颜色。
修改粒子系统的Draw方法:
Code
public override void Draw(GameTime gameTime)
{
foreach (Particle particle in particles)
{
if (particle.isActive)
{
Matrix world = Matrix.CreateTranslation(particle.Position.X, particle.Position.Y, particle.Position.Z);
balleffect.CurrentTechnique=balleffect.Techniques[0];
balleffect.Parameters["World"].SetValue(world);
balleffect.Parameters["View"].SetValue(game.camera.View);
balleffect.Parameters["Projection"].SetValue(game.camera.Projection);
balleffect.Parameters["color"].SetValue(particle.color.ToVector4());
balleffect.Parameters["EyePos"].SetValue(game.camera.mposi);
foreach (ModelMesh mesh in sphere.Meshes)
{
foreach (ModelMeshPart part in mesh.MeshParts)
{
part.Effect = balleffect;
}
mesh.Draw();
}
}
}
base.Draw(gameTime);
}
最终效果:
从下边和从远处的水平面看到的效果