使用基本openGL(非GLSL)实现一个面向对象风格粒子系统。
粒子系统基本有两部分构成
粒子类代表每个粒子,只有属性没有方法,如下:
class Particle {
public:
GLfloat life; //剩余生命
GLfloat fade; //衰减速度
//颜色
GLfloat r;
GLfloat g;
GLfloat b;
//位置
GLfloat x;
GLfloat y;
GLfloat z;
//速度
GLfloat xVel;
GLfloat yVel;
GLfloat zVel;
//加速度
GLfloat xAcc;
GLfloat yAcc;
GLfloat zAcc;
};
这就是本粒子系统中粒子的全部属性。而粒子发生器通过控制每个例子的以上属性来控制整个粒子群。
发生器是一个基类,而通过其不同的继承类实现不同效果的粒子系统。基类如下:
class Generator{
public:
//粒子初始化与再造函数
virtual void particleInit(int i)=0;
//粒子更新函数
virtual void particleUpdate(int i)=0;
//粒子渲染函数
void particleDisplay(int i){
glColor4f(
particles[i].r,
particles[i].g,
particles[i].b,
particles[i].life/particleLife
);
glPointSize(particleSize);
glBegin(GL_POINTS);
glVertex3f(
particles[i].x,
particles[i].y,
particles[i].z);
glEnd();
}
//发生器循环函数
void particleMainLoop(){
for(int i = 0; i < particleCount; i++){
particleDisplay(i);
particleUpdate(i);
}
}
protected:
//粒子数量与对象数组
GLuint particleCount;
Particle* particles;
//粒子颜色组长度与颜色数组
GLuint colorCount;
GLuint (*colorList)[3];
//粒子系统偏移位置 (以(0,0,0)为参照)
GLfloat baseX, baseY, baseZ;
//缺省生命
GLfloat particleLife;
//缺省大小
GLfloat particleSize;
};
其中两个虚函数分别为粒子的初始化与更新函数。一般情况下,只需要定义这两个函数,就可以实现不同效果粒子系统。
下面以下雨系统做示范,即构建一个 RainGenerator
首先需要一个颜色数组,用以粒子运动过程中的颜色变化。假设预设10中颜色,列出数组,粒子由生到死的过程就在这个数组从头到尾过度。
const int colorCount_rain = 10;
GLuint colorArray0 [colorCount_rain][3] = {
0,0,100, //r,g,b
0,0,255,
0,10,255,
0,20,255,
0,35,255,
0,60,255,
0,80,255,
0,100,255,
0,110,255,
0,120,255
};
class RainGenerator: public Generator{
public:
RainGenerator(GLfloat baseX, GLfloat baseY, GLfloat baseZ){
//为位置偏移量赋值
this->baseX = baseX;
this->baseY = baseY;
this->baseZ = baseZ;
//定义粒子数量
//根据整体效果以及系统性能选取
particleCount=1000;
particles = new Particle[particleCount];
//为颜色数组赋值
colorList = colorArray_rain;
colorCount = colorCount_rain;
particleSize = 6;
particleLife = 10;
}
~RainGenerator(){
delete [] particles;
}
virtual void particleInit(int i){
//雨水降落的范围
//定为以基本位置为中心的 40*20 区域降落
float xRange = 40;
float zRange = 20;
//初始化各项属性
particles[i].life = particleLife*(rand()%100) * 0.01f;
particles[i].fade = 0.006f;
particles[i].r = colorList[colorCount-1][0]/255.0f;
particles[i].g = colorList[colorCount-1][1]/255.0f;
particles[i].b = colorList[colorCount-1][2]/255.0f;
particles[i].x = (float)(xRange/2 - xRange*(rand()%100)*0.01);
particles[i].y = 0;
particles[i].z = (float)(zRange/2 - zRange*(rand()%100)*0.01);
particles[i].xVel = 0;
particles[i].yVel = -0.05;
particles[i].zVel = 0;
particles[i].xAcc = 0;
particles[i].yAcc = 0;
particles[i].zAcc = 0;
//加上偏移量
particles[i].x += baseX;
particles[i].y += baseY;
particles[i].z += baseZ;
}
virtual void particleUpdate(int i){
if(particles[i].life < 0.0f){
particleInit(i);
}
else {
//更新位置
particles[i].x += particles[i].xVel;
particles[i].y += particles[i].yVel;
particles[i].z += particles[i].zVel;
//更新速度(若不需要加速度可以注释掉这几句)
particles[i].xVel += particles[i].xAcc;
particles[i].yVel += particles[i].yAcc;
particles[i].zVel += particles[i].zAcc;
//更新生命
particles[i].life -= particles[i].fade;
//更新颜色
//这种方法下颜色显示的顺序是从颜色数组的最后到开头
GLuint colorPos = (GLuint)(particles[i].life/particleLife * (colorCount -1));
particles[i].r = colorList[colorPos][0]/255.0f;
particles[i].g = colorList[colorPos][1]/255.0f;
particles[i].b = colorList[colorPos][2]/255.0f;
}
}
};
particles[i].x = (float)(1 - rand()%20*0.1);
particles[i].xVel = particles[i].x /10; //制造向四周发散效果
particles[i].x += baseX; //再加上偏移量
最后再加上一个集成器,方便同时初始化多个相同的粒子系统。
class ParticleSystem{
public:
ParticleSystem(int numOfGener, int *xpos, int *ypos, int *zpos, int SYSTYPE){
generCount = numOfGener;
switch(SYSTYPE){
case RAINTYPE:
geners = (Generator**) new RainGenerator* [n];
for(int i = 0; iparticleMainLoop();
//并且在适当的地方flush
}
private:
int generCount;
Generator ** geners;
};
需要定义可选的系统类型
#define RAINTYPE 0
#define FIRETYPE 1
#define SNOWTYPE 2
int xpos[] = {70,10,-80,16};
int ypos[] = {20,20,20,40};
int zpos[] = {10,10,30,-10};
ParticleSystem * rain;
rain = new ParticleSystem(4, xpso, ypos, zpos, RAINTYPE);
就在 (70,20,10) 等四个地方加上了落雨的系统。
最后,只需要在 display 函数里面调用主循环函数
rain->particleSystemMainLoop();
并且 flush 就可以了。
是不是很方便?
给出效果图,是之前做作业时实现的几种系统,献丑。
雨落远看
雨落近看
太阳(由一个火球和一个放射系统构成)
银河
感觉实现的这几个效果还是比较可观。粒子系统能干很多事情,只受想象限制。
另外此处不用GLSL完全是为了方便。有条件还是用GLSL会快一些。
本文有些代码是写博客时即时写的,不敢确保一定没有编译错误和笔误,还望指正。
参考:dengchao66的专栏