理论基础
1,粒子系统的基本思想是用许多形状简单且赋予生命的微小粒子作为基本元素来表示物体(一般由点或很小的多边形通过纹理贴图表示),侧重于物体的总体形态和特征的动态变化。把物体定义为许多不规则,随机分布的粒子,且每个粒子均有一定的生命周期。随着时间的推移,旧的粒子不断消失(死亡),新的粒子不断加入(生长)。粒子的这种出生,成长,衰老和死亡的过程,能够较好地反映模糊物体的动态特性。因此它比起传统的帧动画,效果要更加逼真而且节省资源。但其计算量较大,不适合大范围的特效(卡),一般用于较小场景。
2,粒子系统实现的一般步骤:一,初始化,即创建很多粒子对象同时赋予初始属性。二,循环运行,渲染粒子<–>更新粒子(如果有死亡就把其初始化为起始属性)。总的原理就是随机改变粒子的属性,然后渲染,这样循环。所谓的粒子编辑器无非就是在操作改变粒子属性罢了。实例代码
1,雪花粒子
资源:
snowball.bmpwall.bmp
粒子类
//
// Particle.h
// opengl
//
// Created by app05 on 15-5-20.
// Copyright (c) 2015年 app05. All rights reserved.
//说明:就是根据粒子结构给其设置与得到对应属性的值(这个类只是给粒子设置属性)
#ifndef __opengl__Particle__
#define __opengl__Particle__
#include "GLTools.h"
/** 粒子结构 */
struct Particle
{
float x,y,z; /**< 粒子的位置 */
unsigned int r,g,b; /**< 粒子的颜色 */
float vx,vy,vz; /**< 粒子的速度(x,y,z方向) */
float ax,ay,az; /**< 粒子在x,y,z上的加速度 */
float lifetime; /**< 粒子生命值 */
float size; /**< 粒子尺寸 */
float dec; /**< 粒子消失的速度 */
};
/** 粒子类 */
class CParticle
{
private:
Particle* data; /**< 粒子指针 */
int numparticle; /**< 粒子数目 */
public:
CParticle(); /**< 构造函数 */
~CParticle(); /**< 析构函数 */
/** 创建粒子数组 */
int Create(long num);
/** 设置和获取颜色属性 */
int SetColor(GLint r,GLint g,GLint b);
int SetColor(GLint index,GLint r,GLint g,GLint b);
int GetColor(GLint index,GLint &r,GLint &g,GLint &b);
/** 设置和获取速度属性 */
int SetVelocity(GLfloat vx,GLfloat vy,GLfloat vz);
int SetVelocity(GLint index,GLfloat vx,GLfloat vy,GLfloat vz);
int GetVelocity(GLint index,GLfloat &vx,GLfloat &vy,GLfloat &vz);
/** 设置和获取位置属性 */
int SetPosition(GLfloat x,GLfloat y,GLfloat z);
int SetPosition(GLint index,GLfloat x,GLfloat y,GLfloat z);
int GetPosition(GLint index,GLfloat &x,GLfloat &y,GLfloat &z);
/** 设置和获取加速度属性 */
int SetAcceleration(GLfloat ax,GLfloat ay,GLfloat az);
int SetAcceleration(GLint index,GLfloat ax,GLfloat ay,GLfloat az);
int GetAcceletation(GLint index,GLfloat &ax,GLfloat &ay,GLfloat &az);
/** 设置和获取尺寸属性 */
int SetSize(GLfloat size);
int SetSize(GLint index,GLfloat size);
int GetSize(GLint index,GLfloat &size);
/** 设置和获取消失速度属性 */
int SetDec(GLfloat dec);
int SetDec(GLint index,GLfloat dec);
int GetDec(GLint index,GLfloat &dec);
/** 设置和获取生命值属性 */
int SetLifeTime(GLfloat lifetime);
int SetLifeTime(GLint index,GLfloat lifetime);
int GetLifeTime(GLint index,GLfloat &lifetime);
/** 获取粒子数组地址 */
Particle *GetParticle() { return data; }
/** 获得粒子的数目 */
int GetNumOfParticle() { return numparticle; }
/** 获得粒子所有的属性 */
int GetAll(int index, /**< 索引 */
GLint &r,GLint &g,GLint &b, /**< 粒子的颜色 */
GLfloat &x,GLfloat &y,GLfloat &z, /**< 位置 */
GLfloat &vx,GLfloat &vy,GLfloat &vz, /**< 速度 */
GLfloat &ax,GLfloat &ay,GLfloat &az, /**< 加速度 */
GLfloat &size, /**< 大小 */
GLfloat &lifetime, /**< 生命时间 */
GLfloat &dec /**< 消失速度 */
);
/** 设置粒子的所有属性 */
int SetAll(int index, /**< 索引 */
GLint r,GLint g,GLint b, /**< 粒子的颜色 */
GLfloat x,GLfloat y,GLfloat z, /**< 位置 */
GLfloat vx,GLfloat vy,GLfloat vz, /**< 速度 */
GLfloat ax,GLfloat ay,GLfloat az, /**< 加速度 */
GLfloat size, /**< 大小 */
GLfloat lifetime, /**< 生命时间 */
GLfloat dec /**< 消失速度 */
);
};
#endif /* defined(__opengl__Particle__) */
//
// Particle.cpp
// opengl
//
// Created by app05 on 15-5-20.
// Copyright (c) 2015年 app05. All rights reserved.
//
#include "Particle.h"
/** 构造函数 */
CParticle::CParticle()
{
data = NULL;
numparticle = 0;
}
/** 析构函数 */
CParticle::~CParticle()
{
delete []data;
data = NULL;
}
/** 创建一个包含num个元素的粒子数组 */
int CParticle::Create(long num)
{
/** 删除粒子数组 */
if (data)
delete []data;
/** 创建数组 */
if(data = new Particle[num])
{
memset(data,0,sizeof(Particle)*numparticle);
numparticle = num;
/** 返回粒子个数 */
return numparticle;
}
return 0;
}
/** 设置和获取颜色Color的函数实现 */
int CParticle::SetColor(GLint r,GLint g,GLint b)
{
for (int index=0;index<numparticle;++index)
{
data[index].r=r;
data[index].g=g;
data[index].b=b;
}
return true;
}
int CParticle::SetColor(GLint index,GLint r,GLint g,GLint b)
{
if(index>=0 && index<numparticle)
{
data[index].r=r;
data[index].g=g;
data[index].b=b;
return true;
}
return false;
}
int CParticle::GetColor(GLint index,GLint &r,GLint &g,GLint &b)
{
if(index>=0 && index<numparticle)
{
r=data[index].r;
g=data[index].g;
b=data[index].b;
return true;
}
return false;
}
/** 设置和获取位置Position的函数实现 */
int CParticle::SetPosition(GLfloat x,GLfloat y,GLfloat z)
{
for(int index=0;index<numparticle;++index)
{
data[index].x=x;
data[index].y=y;
data[index].z=z;
}
return true;
}
int CParticle::SetPosition(GLint index,GLfloat x,GLfloat y,GLfloat z)
{
if(index>=0 && index<numparticle)
{
data[index].x=x;
data[index].y=y;
data[index].z=z;
return true;
}
return false;
}
int CParticle::GetPosition(GLint index,GLfloat &x,GLfloat &y,GLfloat &z)
{
if(index>=0 && index<numparticle)
{
x=data[index].x;
y=data[index].y;
z=data[index].z;
return true;
}
return false;
}
/** 设置和获取加速度Acceleration的函数实现 */
int CParticle::SetAcceleration(GLfloat ax,GLfloat ay,GLfloat az)
{
for (int index=0;index<numparticle;++index)
{
data[index].ax=ax;
data[index].ay=ay;
data[index].az=az;
}
return true;
}
int CParticle::SetAcceleration(GLint index,GLfloat ax,GLfloat ay,GLfloat az)
{
if(index>=0 && index<numparticle)
{
data[index].ax=ax;
data[index].ay=ay;
data[index].az=az;
return true;
}
return false;
}
int CParticle::GetAcceletation(GLint index,GLfloat &ax,GLfloat &ay,GLfloat &az)
{
if(index>=0 && index<numparticle)
{
ax=data[index].ax;
ay=data[index].ay;
az=data[index].az;
return true;
}
return false;
}
/** Velocity函数的实现 */
int CParticle::SetVelocity(GLfloat vx,GLfloat vy,GLfloat vz)
{
for (int index=0;index<numparticle;++index)
{
data[index].vx=vx;
data[index].vy=vy;
data[index].vz=vz;
}
return true;
}
int CParticle::SetVelocity(GLint index,GLfloat vx,GLfloat vy,GLfloat vz)
{
if(index>=0 && index<numparticle)
{
data[index].vx=vx;
data[index].vy=vy;
data[index].vz=vz;
return true;
}
return false;
}
int CParticle::GetVelocity(GLint index,GLfloat &vx,GLfloat &vy,GLfloat &vz)
{
if(index>=0 && index<numparticle)
{
vx=data[index].vx;
vy=data[index].vy;
vz=data[index].vz;
return true;
}
return false;
}
/** Size函数的实现 */
int CParticle::SetSize(GLfloat size)
{
for (int index=0;index<numparticle;++index)
{
data[index].size=size;
}
return true;
}
int CParticle::SetSize(GLint index,GLfloat size)
{
if (index>=0 && index<numparticle)
{
data[index].size=size;
return true;
}
return false;
}
int CParticle::GetSize(GLint index,GLfloat &size)
{
if(index >= 0 && index < numparticle)
{
size=data[index].size;
return true;
}
return false;
}
/** 消失速度Dec函数 */
int CParticle::SetDec(GLfloat dec)
{
for (int index=0;index<numparticle;++index)
{
data[index].dec=dec;
}
return true;
}
int CParticle::SetDec(GLint index,GLfloat dec)
{
if(index >= 0 && index < numparticle)
{
data[index].dec=dec;
return true;
}
return false;
}
int CParticle::GetDec(GLint index,GLfloat &dec)
{
if(index >= 0 && index < numparticle)
{
dec=data[index].dec;
return true;
}
return false;
}
/** 设置粒子的lifetime 属性 */
int CParticle::SetLifeTime(GLfloat lifetime)
{
for (int index=0;index<numparticle;++index)
{
data[index].lifetime=lifetime;
}
return true;
}
int CParticle::SetLifeTime(GLint index,GLfloat lifetime)
{
if(index >= 0 && index < numparticle)
{
data[index].lifetime=lifetime;
return true;
}
return false;
}
/** 获得粒子的lifetime属性 */
int CParticle::GetLifeTime(GLint index,GLfloat &lifetime)
{
if(index >= 0 && index < numparticle)
{
lifetime=data[index].lifetime;
return true;
}
return false;
}
/** 获取粒子的所有属性 */
int CParticle::GetAll(int index,GLint &r,GLint &g,GLint &b, /**< 粒子的颜色 */
GLfloat &x,GLfloat &y,GLfloat &z, /**< 位置 */
GLfloat &vx,GLfloat &vy,GLfloat &vz, /**< 速度 */
GLfloat &ax,GLfloat &ay,GLfloat &az, /**< 加速度 */
GLfloat &size, /**< 大小 */
GLfloat &lifetime, /**< 生命时间 */
GLfloat &dec /**< 消失速度 */
)
{
if (index>=0 && index<numparticle)
{
r=data[index].r;
g=data[index].g;
b=data[index].b;
x=data[index].x;
y=data[index].y;
z=data[index].z;
vx=data[index].vx;
vy=data[index].vy;
vz=data[index].vz;
ax=data[index].ax;
ay=data[index].ay;
az=data[index].az;
lifetime=data[index].lifetime;
size=data[index].size;
dec=data[index].dec;
return true;
}
return false;
}
/** 设置粒子的所有属性 */
int CParticle::SetAll(int index,GLint r,GLint g,GLint b, /**< 粒子的颜色 */
GLfloat x,GLfloat y,GLfloat z, /**< 位置 */
GLfloat vx,GLfloat vy,GLfloat vz, /**< 速度 */
GLfloat ax,GLfloat ay,GLfloat az, /**< 加速度 */
GLfloat size, /**< 大小 */
GLfloat lifetime, /**< 生命时间 */
GLfloat dec /**< 消失速度 */
)
{
if(index>=0 && index<numparticle)
{
data[index].r=r;
data[index].g=g;
data[index].b=b;
data[index].x=x;
data[index].y=y;
data[index].z=z;
data[index].vx=vx;
data[index].vy=vy;
data[index].vz=vz;
data[index].ax=ax;
data[index].ay=ay;
data[index].az=az;
data[index].lifetime=lifetime;
data[index].size=size;
data[index].dec=dec;
return true;
}
return false;
}
实现
#include "GLTools.h"
#include "Particle.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
/** 创建一个粒子类对象 */
CParticle Snow;
/** 用来设置粒子的属性值 */
float x,y,z,vx,vy,vz,ax,ay,az,size,lifetime,dec;
int r,g,b;
/** 载入纹理 */
GLuint texName[2];
bool LoadTextures()
{
GLbyte *pBits[2];
int nWidth, nHeight;
char* fileName[] = {"/Users/app05/Desktop/opengl/wall.bmp","/Users/app05/Desktop/opengl/snowball.bmp" };
for(int i=0; i<2; i++)
{
pBits[i] = gltReadBMPBits(fileName[i], &nWidth, &nHeight );
if(pBits[i] == NULL)
{
printf("bmp load failed");
exit(-1);
}
glGenTextures(1,&texName[i]);
/** 创建纹理对象 */
glBindTexture(GL_TEXTURE_2D, texName[i]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, nWidth, nHeight,
GL_RGB, GL_UNSIGNED_BYTE,pBits[i]);
}
return true;
}
/** 绘制地面 */
float angle = 0;
void DrawGround()
{
glPushMatrix();
glTranslatef(0.0f,0.0f,-6.0f);
glRotatef(angle,0.0f,1.0f,0.0f);
/** 指定纹理 */
glBindTexture(GL_TEXTURE_2D,texName[0]);
glColor4ub(255,255,255,255);
glNormal3f(0.0f,1.0f,0.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f);glVertex3f(-2.5f,-1.0f,2.5f);
glTexCoord2f(1.0f,0.0f);glVertex3f(-2.5f,-1.0f,-2.5f);
glTexCoord2f(1.0f,1.0f);glVertex3f(2.5f,-1.0f,-2.5f);
glTexCoord2f(0.0f,1.0f);glVertex3f(2.5f,-1.0f,2.5f);
glEnd();
glPopMatrix();
/** 地面转动的角度 */
angle += 0.02f;
}
/** 初始化雪花粒子 */
bool InitSnow()
{
for (int i=0; i < Snow.GetNumOfParticle(); ++i)
{
///初始化颜色(白色)
r = 255;
g = 255;
b = 255;
Snow.SetColor(i,r,g,b);
///初始化坐标
x = 0.1f * (rand() % 50) - 2.5f;
y = 2 + 0.1f * (rand() % 2);
if((int)x % 2 == 0)
z = rand()%6;
else
x = -rand()%3;
Snow.SetPosition(i,x,y,z);
///初始化速度
vx = 0.00001 * (rand()%100);
vy = 0.0000002 * (rand()%28000);
vz = 0;
Snow.SetVelocity(i,vx,vy,vz);
///初始化加速度
ax = 0;
ay = 0.000005f;
az = 0;
Snow.SetAcceleration(i,ax,ay,az);
///初始化生命周期
lifetime = 100;
Snow.SetLifeTime(i,lifetime);
///消失速度
dec = 0.005 * (rand()%50);
Snow.SetDec(i,dec);
///初始化大小
Snow.SetSize(i,0.03f);
}
return true;
}
/** 更新粒子 */
void UpdateSnow()
{
/** 更新位置 */
x += (vx * 5);
y -= vy;
/** 更新速度 */
vy += ay;
/** 更新生存时间 */
lifetime -= dec;
if(x > 3)
x = -2;
/** 如果粒子消失或生命结束 */
if (y <= -1 || lifetime <= 0)
{
/** 初始化位置 */
x = 0.1f * (rand()%50) - 2.5f;
y = 2 + 0.1f * (rand()%2);
if((int)x%2 == 0)
z = rand()%6;
else
z = -rand()%3;
/** 初始化速度 */
vx = (float)(0.00001 * (rand()%100));
vy = (float)(0.0000002 * (rand()%28000));
vz = 0;
/** 初始化加速度 */
ax = 0;
ay = 0.000005f;
az = 0;
lifetime = 100;
dec = 0.005*(rand()%50);
}
}
/** 绘制粒子 */
void DrawParticle()
{
/** 绑定纹理 */
glBindTexture(GL_TEXTURE_2D,texName[1]);
for(int i = 0; i<Snow.GetNumOfParticle(); ++i)
{
/** 获得粒子的所有属性 */
Snow.GetAll(i,r,g,b,x,y,z,vx,vy,vz,ax,ay,az,size,lifetime,dec);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-6.0f);
glColor4ub(r,g,b,255);
glNormal3f(0.0f,0.0f,1.0f); /**< 定义法线方向 */
/** 画出粒子 */
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f);glVertex3f(x-size,y-size,z);
glTexCoord2f(1.0f,0.0f);glVertex3f(x-size,y+size,z);
glTexCoord2f(1.0f,1.0f);glVertex3f(x+size,y+size,z);
glTexCoord2f(0.0f,1.0f);glVertex3f(x+size,y-size,z);
glEnd();
/** 更新粒子属性 */
UpdateSnow();
Snow.SetAll(i,r,g,b,x,y,z,vx,vy,vz,ax,ay,az,size,lifetime,dec);
}
glutPostRedisplay();//发送渲染请求
}
/
/** 初始化 */
void init(void)
{
/** 用户自定义的初始化过程 */
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);//指定深度缓冲区的清除值(即将深度缓冲区里的值设置为1)
glDepthFunc(GL_LEQUAL);//指定用于深度缓冲比较值(即新进像素深度值与原来的1比较,<=则通过,否则丢弃)
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_TEXTURE_2D); /**< 开启纹理映射 */
glBlendFunc(GL_SRC_ALPHA,GL_ONE); /**< 设置混合因子获得半透明效果 */
glEnable(GL_BLEND); /**< 启用混和 */
/** 载入纹理 */
if(!LoadTextures())
{
printf("bmp load failed");
exit(-1);
}
/** 创建500个粒子 */
Snow.Create(500);
/** 初始化粒子 */
InitSnow();
}
/** 渲染 */
void display(void)
{
/** 用户自定义的绘制过程 */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
/** 绘制地面 */
DrawGround();
/** 绘制粒子 */
DrawParticle();
glFlush(); /**<强制执行所有的OpenGL命令 */
}
void ChangeSize(int width, int height)
{
glViewport(0, 0, width, height); /**< 重新设置视口 */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 1700.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB );
glutInitWindowSize (400, 400);
glutInitWindowPosition (100, 100);
glutCreateWindow("雪花粒子");
init();
glutReshapeFunc(ChangeSize);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
2,烟花粒子
资源
flare.bmp
/** 初始化粒子 */
bool InitProtechny()
{
for (int i=0; i < Protechny.GetNumOfParticle(); ++i)
{
///初始化颜色
//r = rand()%255;
//g = rand()%255;
//b = rand()%255;
r = 50;
g = 50;
b = 100;
Protechny.SetColor(i,r,g,b);
///初始化坐标
x = 0.005f * (rand()%9);
y = 0.005f * (rand()%9)-1;
z = 0.005f * (rand()%6);
Protechny.SetPosition(i,x,y,z);
///初始化速度
vx = 0.0000007f * (rand()%9000-rand()%9000);
vy = 0.0000042f * (rand()%9000);
vz = 0.0000001f * (rand()%9000);
Protechny.SetVelocity(i,vx,vy,vz);
///初始化加速度
ax = 0;
ay = -0.0002;
az = 0;
Protechny.SetAcceleration(i,ax,ay,az);
///初始化生命周期
lifetime = 100;
Protechny.SetLifeTime(i,lifetime);
///消失速度
dec=0.05*(rand()%50);
Protechny.SetDec(i,dec);
///初始化大小
Protechny.SetSize(i,0.02f);
}
return true;
}
/** 更新粒子 */
void UpdateProtechny()
{
/** 更新位置 */
x += vx;
y += vy;
z += vz;
/** 更新速度 */
vy += ay;
/** 更新生存时间 */
lifetime -= dec;
/** 如果粒子消失或生命结束 */
if (y <= -1 || lifetime <= 0)
{
/** 初始化位置 */
x = 0.005f * (rand()%9);
y = 0.005f * (rand()%9)-1;
z = 0.000001f * (rand()%9000);
/** 初始化速度 */
vx = 0.0000007f * (rand()%9000-rand()%9000);
vy = 0.0000042f * (rand()%9000);
vz = 0.0000001f * (rand()%90000);
lifetime = 100;
dec = 0.1 * (rand()%50);
}
}
3,瀑布粒子(资源同上)
/** 初始化粒子 */
bool InitParticle()
{
for (int i=0; i < Waterfall.GetNumOfParticle(); ++i)
{
///初始化颜色
//r = rand()%255;
//g = rand()%255;
//b = rand()%255;
r = 50;
g = 50;
b = 100;
Waterfall.SetColor(i,r,g,b);
///初始化坐标
x = 0.005f * (rand()%9) - 1;
y = 0.005f * (rand()%9) + 1.6;
z = 0.0001f * (rand()%15000);
Waterfall.SetPosition(i,x,y,z);
///初始化速度
vz = 0;
vx = 0.000001f*(rand()%9000);
vy = -rand()%5400 * vx * vx;
Waterfall.SetVelocity(i,vx,vy,vz);
///初始化加速度
ax = 0;
ay = -0.0001;
az = 0;
Waterfall.SetAcceleration(i,ax,ay,az);
///初始化生命周期
lifetime = 100;
Waterfall.SetLifeTime(i,lifetime);
///消失速度
dec = 0.05 * (rand()%50);
Waterfall.SetDec(i,dec);
///初始化大小
Waterfall.SetSize(i,0.02f);
}
return true;
}
/** 更新粒子 */
void UpdateParticle()
{
/** 更新位置 */
x += vx;
y += vy;
/** 更新速度 */
vy += ay;
/** 更新生存时间 */
lifetime -= dec;
/** 如果粒子消失或生命结束 */
if (y <= -1 || lifetime <= 0)
{
/** 初始化位置 */
x = 0.005f * (rand()%9) - 1;
y = 0.005f * (rand()%9) + 1.6;
z = 0.0001f * (rand()%15000);
/** 初始化速度 */
vz = 0;
vx = 0.000001f * (rand()%9000);
vy = -rand()%500 * vx * vx;
lifetime = 100;
dec = 0.05 * (rand()%50);
}
}