(2条消息) OpenGL进阶(六) 粒子系统
http://blog.csdn.net/qp120291570/article/details/8373896
一、提要
有一款例子特效软件叫做particle illution,在影视后期和游戏制作领域都可以用到,相信很多人都接触过,今天我们用SDL+OpenGL来实现例子效果。
确保你搞定了物理模拟的代码!
二、原理简介
所谓的例子系统,就是同时控制一大堆类似的对象,这些对象可能是形体,可能是图片,有着不同的特征(寿命,速度,位置)。有了之前的基础,我们可以很轻易地搞定今天的东西。
三、代码清单
首先是粒子的头文件,我直接写成结构体了,里面有一些基本的属性。
- /*****************************************************************************
- Copyright: 2012, ustc All rights reserved.
- contact:k283228391@126.com
- File name: particle.h
- Description:Partical in opengl.
- Author:Silang Quan
- Version: 1.0
- Date: 2012.12.20
- *****************************************************************************/
- #ifndef PARTICLE_H
- #define PARTICLE_H
- #include "vector3d.h"
- typedef struct
- {
- float r;
- float g;
- float b;
- float alpha;
- }Color;
- typedef struct
- {
- Vector3D position;
- Vector3D velocity;
- Vector3D acceleration;
- Color color;
- float age;
- float life;
- float size;
- }Particle;
- #endif // PARTICLE_H
/*****************************************************************************Copyright: 2012, ustc All rights reserved.contact:k283228391@126.comFile name: particle.hDescription:Partical in opengl.Author:Silang QuanVersion: 1.0Date: 2012.12.20*****************************************************************************/#ifndef PARTICLE_H#define PARTICLE_H#include "vector3d.h"typedef struct{float r;float g;float b;float alpha;}Color;typedef struct{Vector3D position;Vector3D velocity;Vector3D acceleration;Color color;float age;float life;float size;}Particle;#endif // PARTICLE_H
我们用球体来模拟例子,所以size表示的就是球体的半径。
接下来是粒子系统类(类名拼写错了*-*)
[cpp] view plain copy print ?/*****************************************************************************Copyright: 2012, ustc All rights reserved.contact:k283228391@126.comFile name: particalsystem.hDescription:Partical in opengl.Author:Silang QuanVersion: 1.0Date: 2012.12.20*****************************************************************************/#ifndef PARTICALSYSTEM_H#define PARTICALSYSTEM_H#include <vector>#include <math.h>#include <time.h>#include <stdlib.h>#include <iostream>#include <GL/gl.h>#include <GL/glu.h>#include "particle.h"#define PI 3.1415926using namespace std;class ParticalSystem{public:ParticalSystem();ParticalSystem(int _count,float _gravity){ptlCount=_count;gravity=_gravity;};void init();void simulate(float dt);void aging(float dt);void applyGravity();void kinematics(float dt);void render();virtual ~ParticalSystem();protected:private:int ptlCount;float gravity;GLUquadricObj *mySphere;vector<Particle> particles;};#endif // PARTICALSYSTEM_H
/*****************************************************************************Copyright: 2012, ustc All rights reserved.contact:k283228391@126.comFile name: particalsystem.hDescription:Partical in opengl.Author:Silang QuanVersion: 1.0Date: 2012.12.20*****************************************************************************/#ifndef PARTICALSYSTEM_H#define PARTICALSYSTEM_H#include <vector>#include <math.h>#include <time.h>#include <stdlib.h>#include <iostream>#include <GL/gl.h>#include <GL/glu.h>#include "particle.h"#define PI 3.1415926using namespace std;class ParticalSystem{public:ParticalSystem();ParticalSystem(int _count,float _gravity){ptlCount=_count;gravity=_gravity;};void init();void simulate(float dt);void aging(float dt);void applyGravity();void kinematics(float dt);void render();virtual ~ParticalSystem();protected:private:int ptlCount;float gravity;GLUquadricObj *mySphere;vector<Particle> particles;};#endif // PARTICALSYSTEM_H
解释一下几个重要函数:
init:做一些例子系统的初始化工作;
aging:计算粒子的年龄;
applyGravity:向粒子施加重力;
kinematics:这个单词的意思是运动学,所以就是负责管理粒子的加速,位移;
simulate:例子模拟的总负责函数;
render:渲染粒子;
然后来看函数是怎么实现的:
[cpp] view plain copy print ?/*****************************************************************************Copyright: 2012, ustc All rights reserved.contact:k283228391@126.comFile name: particalsystem.CppDescription:Partical in opengl.Author:Silang QuanVersion: 1.0Date: 2012.12.22*****************************************************************************/#include "particalsystem.h"ParticalSystem::ParticalSystem(){//ctor}ParticalSystem::~ParticalSystem(){//dtor}void ParticalSystem::init(){int i;srand(unsigned(time(0)));Color colors[3]={{0,0,1,1},{1,0,1,1}};for(i=0;i<ptlCount;i++){//theta =(rand()%361)/360.0* 2*PI;Particle tmp={Vector3D(0,0,0),Vector3D(((rand()%50)-26.0f),((rand()%50)-26.0f),((rand()%50)-26.0f)),Vector3D(0,0,0),colors[rand()%2],0.0f,0.5+0.05*(rand()%10),0.3f};particles.push_back(tmp);}mySphere=gluNewQuadric();}void ParticalSystem::simulate(float dt){aging(dt);applyGravity();kinematics(dt);}void ParticalSystem::aging(float dt){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){iter->age+=dt;if(iter->age>iter->life){iter->position=Vector3D(0,0,0);iter->age=0.0;iter->velocity=Vector3D(((rand()%30)-15.0f),((rand()%30)-11.0f),((rand()%30)-15.0f));}}}void ParticalSystem::applyGravity(){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++)iter->acceleration=Vector3D(0,gravity,0);}void ParticalSystem::kinematics(float dt){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){iter->position = iter->position+iter->velocity*dt;iter->velocity = iter->velocity+iter->acceleration*dt;}}void ParticalSystem::render(){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){float alpha = 1 - iter->age / iter->life;//calculate the alpha value according to the age of particle.Vector3D tmp=iter->position;glColor4f(iter->color.r,iter->color.g,iter->color.b,alpha);glPushMatrix();glTranslatef(tmp.x,tmp.y,tmp.z);gluSphere(mySphere,iter->size, 32, 16);glPopMatrix();}}
/*****************************************************************************Copyright: 2012, ustc All rights reserved.contact:k283228391@126.comFile name: particalsystem.CppDescription:Partical in opengl.Author:Silang QuanVersion: 1.0Date: 2012.12.22*****************************************************************************/#include "particalsystem.h"ParticalSystem::ParticalSystem(){//ctor}ParticalSystem::~ParticalSystem(){//dtor}void ParticalSystem::init(){int i;srand(unsigned(time(0)));Color colors[3]={{0,0,1,1},{1,0,1,1}};for(i=0;i<ptlCount;i++){//theta =(rand()%361)/360.0* 2*PI;Particle tmp={Vector3D(0,0,0),Vector3D(((rand()%50)-26.0f),((rand()%50)-26.0f),((rand()%50)-26.0f)),Vector3D(0,0,0),colors[rand()%2],0.0f,0.5+0.05*(rand()%10),0.3f};particles.push_back(tmp);}mySphere=gluNewQuadric();}void ParticalSystem::simulate(float dt){aging(dt);applyGravity();kinematics(dt);}void ParticalSystem::aging(float dt){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){iter->age+=dt;if(iter->age>iter->life){iter->position=Vector3D(0,0,0);iter->age=0.0;iter->velocity=Vector3D(((rand()%30)-15.0f),((rand()%30)-11.0f),((rand()%30)-15.0f));}}}void ParticalSystem::applyGravity(){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++)iter->acceleration=Vector3D(0,gravity,0);}void ParticalSystem::kinematics(float dt){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){iter->position = iter->position+iter->velocity*dt;iter->velocity = iter->velocity+iter->acceleration*dt;}}void ParticalSystem::render(){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){float alpha = 1 - iter->age / iter->life;//calculate the alpha value according to the age of particle.Vector3D tmp=iter->position;glColor4f(iter->color.r,iter->color.g,iter->color.b,alpha);glPushMatrix();glTranslatef(tmp.x,tmp.y,tmp.z);gluSphere(mySphere,iter->size, 32, 16);glPopMatrix();}}
实现还是比较简单的,下面渲染看一下^^.
首先要在initGL函数中添加两句话:
[cpp] view plain copy print ?glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
这样透明度才会有效。
接着初始化一个例子系统,并对例子进行初始化:
[cpp] view plain copy print ?ParticalSystem ps;
ParticalSystem ps;
- int main( int argc, char* argv[] )
- {
- // Color depth in bits of our window.
- int flags= SDL_OPENGL|SDL_RESIZABLE;
- //Set the SDL
- initSDL(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,flags);
- //Set the OpenGL
- initGL(SCREEN_WIDTH, SCREEN_HEIGHT );
- ps=ParticalSystem(100,-15.0);
- ps.init();
- //main loop
- while(true)
- {
- /* Process incoming events. */
- handleEvents( );
- ps.simulate(0.01);
- /* Draw the screen. */
- renderGL( );
- }
- return 0;
- }
int main( int argc, char* argv[] ){// Color depth in bits of our window.int flags= SDL_OPENGL|SDL_RESIZABLE;//Set the SDLinitSDL(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,flags);//Set the OpenGLinitGL(SCREEN_WIDTH, SCREEN_HEIGHT ); ps=ParticalSystem(100,-15.0); ps.init();//main loopwhile(true){/* Process incoming events. */handleEvents( );ps.simulate(0.01);/* Draw the screen. */renderGL( );}return 0;}
然后是渲染函数:
- void renderGL()
- {
- // Clear the color and depth buffers.
- glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
- // We don't want to modify the projection matrix. */
- glMatrixMode( GL_MODELVIEW );
- glLoadIdentity( );
- // Move down the z-axis.
- glTranslatef(0.0f,0.0f,-35.0f);
- ps.render();
- SDL_GL_SwapBuffers( );
- }
void renderGL(){// Clear the color and depth buffers.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );// We don't want to modify the projection matrix. */glMatrixMode( GL_MODELVIEW );glLoadIdentity( );// Move down the z-axis.glTranslatef(0.0f,0.0f,-35.0f);ps.render();SDL_GL_SwapBuffers( );}
跑一下:

效果还是不错的~下面我们来实现一些更棒的效果!
四、动态模糊和碰撞检测
动态模糊的实现比较简单,主循环不再每次把整个画面清空,而是每帧画一个半透明的黑色长方形,就可以模拟动态模糊(motion blur)的效果。
将之前的
[cpp] view plain copy print ?// Clear the color and depth buffers.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
// Clear the color and depth buffers.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
改成
[cpp] view plain copy print ?// Clear the depth buffers.glClear(GL_DEPTH_BUFFER_BIT );
// Clear the depth buffers.glClear(GL_DEPTH_BUFFER_BIT );
然后在例子系统的render函数中添加画矩形的代码:
[cpp] view plain copy print ?void ParticalSystem::render(){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){float alpha = 1 - iter->age / iter->life;Vector3D tmp=iter->position;glColor4f(iter->color.r,iter->color.g,iter->color.b,alpha);//glColor4f(1.0f, 1.0f, 1.0f, 0.1);glPushMatrix();glTranslatef(tmp.x,tmp.y,tmp.z);gluSphere(mySphere,iter->size, 32, 16);glPopMatrix();}//Motion blueglColor4f(0.0f,0.0f,0.0f,0.1);glBegin(GL_QUADS);glVertex3f(-20.0f , -20.0f , 20.0f );glVertex3f( 20.0f , -20.0f , 20.0f );glVertex3f( 20.0f , 20.0f , 20.0f );glVertex3f(-20.0f , 20.0f , 20.0f );glEnd();}
void ParticalSystem::render(){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){float alpha = 1 - iter->age / iter->life;Vector3D tmp=iter->position;glColor4f(iter->color.r,iter->color.g,iter->color.b,alpha);//glColor4f(1.0f, 1.0f, 1.0f, 0.1);glPushMatrix();glTranslatef(tmp.x,tmp.y,tmp.z);gluSphere(mySphere,iter->size, 32, 16);glPopMatrix();}//Motion blueglColor4f(0.0f,0.0f,0.0f,0.1);glBegin(GL_QUADS);glVertex3f(-20.0f , -20.0f , 20.0f );glVertex3f( 20.0f , -20.0f , 20.0f );glVertex3f( 20.0f , 20.0f , 20.0f );glVertex3f(-20.0f , 20.0f , 20.0f );glEnd();}
渲染一下:

效果还不错!
碰撞检测之前也实现过,在粒子系统里面加检测函数就Ok了.
[cpp] view plain copy print ?void ParticalSystem::checkBump(float x1,float x2,float y1,float y2){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){if (iter->position.x - iter->size < x1 || iter->position.x +iter->size > x2) iter->velocity.x = -iter->velocity.x;if (iter->position.y - iter->size < y1 || iter->position.y + iter->size > y2) iter->velocity.y = -iter->velocity.y;}}
void ParticalSystem::checkBump(float x1,float x2,float y1,float y2){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){if (iter->position.x - iter->size < x1 || iter->position.x +iter->size > x2) iter->velocity.x = -iter->velocity.x;if (iter->position.y - iter->size < y1 || iter->position.y + iter->size > y2) iter->velocity.y = -iter->velocity.y;}}
由于我们的粒子是在空间运动,运行之后我们就可以看到粒子沿着空间的各个方向运动,我们在沿Z轴方向添加了一个“盒子”,撞到盒子则速度做相应的变化。


五、交互发射
这个其实是opengl之外的东西了。我们要用的是SDL的获取鼠标信息函数:SDL_GetMouseState(&posX, &posY);
作用就是获取当前鼠标的位置。
接下来还要做的一件事是写坐标变换函数。因为opengl的坐标个SDL窗口的坐标并不是重合的,我们要将鼠标的当前坐标映射到OpenGL的坐标上去。
[cpp] view plain copy print ?float posTransX(int posX){return (posX-400)/20.0;}float posTransY(int posY){return (400-posY)/20.0;}
float posTransX(int posX){return (posX-400)/20.0;}float posTransY(int posY){return (400-posY)/20.0;}
因为我的窗口是800*800的,然后根据当前视口的位置,转换函数就想上面那样。
坐后要修改一下粒子的simulate函数。当粒子死亡的时候,他的初始位置设置为鼠标当前的位置。
[cpp] view plain copy print ?void ParticalSystem::aging(float dt,float posX,float posY){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){iter->age+=dt;if(iter->age>iter->life){iter->position=Vector3D(posX,posY,0);iter->age=0.0;iter->velocity=Vector3D(((rand()%30)-15.0f),((rand()%30)-11.0f),((rand()%30)-15.0f));}}}
void ParticalSystem::aging(float dt,float posX,float posY){for(vector<Particle>::iterator iter=particles.begin();iter!=particles.end();iter++){iter->age+=dt;if(iter->age>iter->life){iter->position=Vector3D(posX,posY,0);iter->age=0.0;iter->velocity=Vector3D(((rand()%30)-15.0f),((rand()%30)-11.0f),((rand()%30)-15.0f));}}}
效果就像这样:


六、参考
nehe的opengl教程
