基于OpenGL的烟花粒子系统

 
动画内容
本动画模拟烟花的燃放过程。烟花燃放从地面升起,升到空中爆炸并产生无数小烟花围成圆形并落下,下落过程中伴随这能量的衰减直到消失。此动画的视角为仰视大致为30°。
名词解释
VC++6.0:一种开发环境;
OpenGL: 是个专业的开放的 3D 程序接口,是一个功能强大,调用方便的底层 3D 图形库;
glut OpenGL Utility Toolkit
粒子系统:是许多粒子的集合。在图像学领域里,人们利用粒子系统模拟各种各样的现象,如火花、飞行的鸟群、波浪等。在这些应用中,粒子系统的动力学规律确定了粒子的位置,在每个位置上,我们放置了一个图形对象,而不是一个点。 [1]
运行环境
硬件要求
奔腾4 2.0GHZ以上,显卡X550(128bit位宽,128MB显存)以上。
软件要求
Windows2K/XP操作系统、VC++6.0 、并支持OpenGL glut工具包。
详细设计
粒子的数据结构定义
typedef struct
{
         float x,y,z;         // 粒子位置
         float xSpeed, ySpeed, zSpeed; // 粒子的速度
         float xg, yg, zg;// 粒子的运动加速度
         float r,g,b;         // 粒子的颜色
         int style;     // 粒子是上升还是下降
}PARTICLES;
烟花的数据结构定义
#define MAX_PARTICLES 24         // 小烟花的个数
#define MAX_TAIL 30       // 烟花尾部长度
#define MAX_FIRE 5        // 烟花的个数
struct
{
         PARTICLES particle[MAX_PARTICLES][MAX_TAIL]; // 烟花系统数组
         GLfloat  life, fade, rad;  // 烟花的生命,衰减速度、和 x z 平面上的运动速度
} Fire[MAX_FIRE];
烟花燃放过程建模及实现
烟花从地面的某一点生成并向上( y 轴方向)作减速运动,在 x z 轴方向速度为 0 。当 y 方向速度为 0 时烟花爆炸生成小烟花。此时给烟花的 x z 平面上的速度 rad 赋初值,并将其分解到 x z 方向上(与坐标轴的夹角根据下标确定), y 轴的加速度不变至此小烟花运动轨迹为抛物线。爆炸后位置坐标变化的数学模型为:
 X = rad*cos(a);
   Y = yspeed + gt;
Z = rad*sin(a);
下面是烟花系统赋初始值( init ()函数主要源代码
for(loop2=0; loop2
{
        // 初始位置
                   xtemp = rand()%30 -15;
                   ytemp = (-1)*rand()%5-8;
                   ztemp = (-1)*rand()%5 -100;
                   speed = rand()%5+10; // x z 平面上的运动速度
                   Fire[loop2].life = 1.0f; 
                   Fire[loop2].fade = (float)(rand()%100)/20000+0.002f;
                   Fire[loop2].rad = rand()%3+4;
                   for (loop=0; loop
                   {
                            Fire[loop2].particle[loop][0].style = 0;
              // 初始位置
                            Fire[loop2].particle[loop][0].x = xtemp;
                            Fire[loop2].particle[loop][0].y = ytemp;
                            Fire[loop2].particle[loop][0].z = ztemp;
              // 初始速度
                            Fire[loop2].particle[loop][0].xSpeed =0.0f;
                            Fire[loop2].particle[loop][0].ySpeed =speed;
                            Fire[loop2].particle[loop][0].zSpeed =0.0f;
              // 初始加速度
                            Fire[loop2].particle[loop][0].xg =0.0f;
                            Fire[loop2].particle[loop][0].yg =-2.0f;
                            Fire[loop2].particle[loop][0].zg =0.0f;
              // 初始颜色
                            Fire[loop2].particle[loop][0].r =1.0f;
                            Fire[loop2].particle[loop][0].g =1.0f;
                            Fire[loop2].particle[loop][0].b =1.0f;
            // 尾部初始化
                            for(loop1=1;loop1
                            {
                                     Fire[loop2].particle[loop][loop1].x=Fire[loop2].particle[loop][0].x;
                                     Fire[loop2].particle[loop][loop1].y=Fire[loop2].particle[loop][0].y;
                                     Fire[loop2].particle[loop][loop1].z=Fire[loop2].particle[loop][0].z;
                            } //for1 end
                   }//for2 end
         }// for3 end
粒子的运动位置变化( idle ()函数主要源代码
for(loop=0;loop
{
// 粒子位置更新
Fire[loop2].particle[loop][0].x += Fire[loop2].particle[loop][0].xSpeed/(speedFator*1000.0f);
Fire[loop2].particle[loop][0].y += Fire[loop2].particle[loop][0].ySpeed/(speedFator*1000.0f);
Fire[loop2].particle[loop][0].z += Fire[loop2].particle[loop][0].zSpeed/(speedFator*1000.0f);
                           
// 粒子速度更新
Fire[loop2].particle[loop][0].ySpeed +=Fire[loop2].particle[loop][0].yg/(speedFator*1000.0f);
if(Fire[loop2].particle[loop][0].style==0 && Fire[loop2].particle[loop][0].ySpeed <2.0)
         {
                  Fire[loop2].particle[loop][0].style = 1;
                   Fire[loop2].particle[loop][0].r = colors[color][0];                                          
                   Fire[loop2].particle[loop][0].g = colors[color][1];
                   Fire[loop2].particle[loop][0].b = colors[color][2];
       // 速度分解
                   Fire[loop2].particle[loop][0].xSpeed = Fire[loop2].rad*sin(loop*15.0/(2*3.14159));
                   Fire[loop2].particle[loop][0].zSpeed = Fire[loop2].rad*cos(loop*15.0/(2*3.14159));
         }
}
         Fire[loop2].life -= Fire[loop2].fade;     // 生命衰减
粒子的死亡与再生
当粒子的生命减到小于 0 时,粒子死亡(显示过程中用生命值作为显示颜色的第四个参数,即 alpha 值)。然后给粒子重新生成,代码与初始化的代码类似。
后期制作
给动画添加背景图片,背景使用东方明珠夜景。为了体现烟花的真实性,在画完背景图片以后开启混合,使得背景的颜色也随烟花的颜色变化。
         glCallList(1);     // 绘制背景
         glEnable(GL_BLEND); // 开启混合
改变视角,使得照相机仰视大致 30 °,给人以身临其境的感觉。
gluLookAt(0.0,-10.-30,0.0,0.0,10.0,-100.0,0.0,1.0,0.0);
        

[1]吴文国译.交互式计算机图形学--基于OpenGL的自顶向下方法(第3版)
 

你可能感兴趣的:(OpenGL学习笔记)