资源描述
课 程 设 计 报告
题 目 基于openGL的粒子系统——模拟火焰
系 (部)
专 业
班 级
学生姓名
学 号
起止时间:
指导教师(签字)
系 主 任(签字)
提交时间:
一、设计任务及要求:
设计任务:
要 求:
此次课程设计的课题为通过编程,实现火焰的绘制。实现交互控制火焰颜色、火焰燃烧方向、贴图形状,通过实验得到火焰贴图大小与火焰效果的关系,并给出代码和结果截图。
指导教师签名:
年 月 日
二、指导教师评语:
指导教师签名:
年 月 日
三、成绩
计算机图形学课程设计报告
1.课程设计目的
本学期系统学习了《计算机图形学》这门专业课,在学期期末按课程要求进行实验。粒子系统由Reeves于1983年首次提出,此后越来越受到重视,它的主要优点是可以利用非常简单的体素来构造复杂的物体,为自然现象(如火焰、雨、雪、树林等)的造型提供了强有力的技术手段。
该课程设计以培养我们算法设计与实现的能力为目标,通过实践,使我们了解、掌握计算机图形学的基本知识和关键技术、了解和熟悉计算机图形学的方法、工具和环境,同时培养我们的思维能力和团队合作能力。
2.课程设计描述及要求
粒子系统的基本思想是用许多形状简单且赋予生命的微小粒子作为基本元素来表示基本物体,侧重于物体的总体形态和特征的动态变化。把物体定义为许多不规则、随机分布的粒子,且每个粒子均有一定的生命周期。随着时间的推移,旧的粒子不断消失(死亡),新的粒子不断加入(生长)。粒子的这种出生、成长、衰老、死亡的过程,能够较好的反应模糊物体的动态特征。一个粒子系统是不断进化的,在生命周期的每一刻,都要完成以下四步:
初始化粒子
更新粒子
删除死粒子
绘制粒子
第一步产生5000个新粒子,他们的每个粒子都有生命周期为2;
第二步根据情况更新,递减一个随机的时间步;
第三步检查粒子的生命期,若为零,则将粒子从系统中删除;
第四步显示粒子系统中的粒子。
要求:
此次课程设计的课题为通过编程,实现火焰的绘制。实现交互控制火焰颜色、火焰燃烧方向、贴图形状,通过实验得到火焰贴图大小与火焰效果的关系,并给出代码和结果截图。
3.系统开发环境
开发工具:VC 6.0
操作系统:Microsoft Windows 7
4、粒子系统(火焰)的算法原理
4.1 定义粒子的属性
宏定义粒子的最大数目5000,并定义结构体来设置粒子属性,代码如下:
#define MAX_PARTICLES 5000 // 粒子数的数目
typedef struct // 创建结构体
{
bool active; //
float life; // 粒子生命周期
float fade; // 消失的速度
float r;
float g;
float b;
float x; // 坐标
float y; // 坐标
float z; // 坐标
float xi;
float yi;
float zi;
float xg; // X轴加速度
float yg; // Y轴加速度
float zg; // Z轴加速度
}
particles;
particles particle[MAX_PARTICLES]; // 创建粒子数组
4.2 初始化粒子
利用循环变量loop,loop小于等于最大粒子数目时,则初始化每一个粒子,代码如下:
for (loop=0;loop<MAX_PARTICLES;loop++)
{
particle[loop].active=true;
particle[loop].life=2.0f; //生命期
particle[loop].fade=float(rand()%100)/1500.0f+0.002f; //消失速度(随机)
particle[loop].r=colors[loop*(4/MAX_PARTICLES)][0];
particle[loop].g=colors[loop*(4/MAX_PARTICLES)][1];
particle[loop].b=colors[loop*(4/MAX_PARTICLES)][2];
particle[loop].xg=0.0f;
particle[loop].yg=0.3f;
particle[loop].zg=0.0f;
}
4.3 粒子消失的过程
(1)定义消失变量,使粒子的生命期递减一个随机的生命步,代码如下:
particle[loop].fade=float(rand()%100)/1500.0f+0.002f;
(2)粒子的颜色透明度随着生命期的减少而降低,最终消失,代码如下:
glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life);
4.4 绘制及更新粒子
(1)设置粒子的运动状态,代码如下:
particle[loop].x+=particle[loop].xi/(slowdown*1050); //X的坐标随X轴速度变化
particle[loop].y+=particle[loop].yi/(slowdown*1050);
particle[loop].z+=particle[loop].zi/(slowdown*1050);
particle[loop].xi+=particle[loop].xg; // X轴的速度随X轴的加速度变化
particle[loop].yi+=particle[loop].yg;
particle[loop].zi+=particle[loop].zg;
particle[loop].life-=particle[loop].fade; // 周期衰减
(2) 当粒子生命期衰减为0时,更新粒子,代码如下:
if (particle[loop].life<0.0f)
{
particle[loop].life=2.0f;
particle[loop].fade=float(rand()%100)/1500.0f+0.002f;
particle[loop].x=0.0f;
particle[loop].y=0.0f;
particle[loop].z=0.0f;
particle[loop].xi=float((rand()%60)-30.0f);
particle[loop].yi=float((rand()%60)-30.0f);
particle[loop].zi=float((rand()%60)-30.0f);
particle[loop].r=colors[col][0]; //渲染
particle[loop].g=colors[col][1]; //渲染
particle[loop].b=colors[col][2]; //渲染
particle[loop].xg=xg1;
particle[loop].yg=yg1;
particle[loop].zg=0.0f;
}
5、实现交互的原理
5.1 键盘和鼠标主菜单控制火焰颜色
(1)定义颜色数组及设置火焰颜色:
static GLfloat colors[8][3]= // 颜色数组
{
{0.0f,0.0f,0.0f},{0.5f,0.75f,1.0f},{1.0f,0.75f,0.5f},{0.75f,1.0f,0.5f},
{1.0f,0.5f,0.75f}, {0.35f,0.8f,0.5f}, {0.55f,0.25f,0.75f},{0.5f,0.5f,0.75f},
};
particle[loop].r=colors[col][0]; //渲染
particle[loop].g=colors[col][1]; //渲染
particle[loop].b=colors[col][2]; //渲染
(2)用键盘的“q”“w”“a”“s”“z”键和主菜单选择数组中相应行的RGB值来改变火焰颜色,代码如下:
void keyboard(unsigned char key,int x,int y) //定义键盘功能
{
switch(key)
{
case 'q':
{
col=3;
xg1+=0.05;
glutPostRedisplay(); //重绘函数
} break;
case 'w':
{
col=4;
yg1+=0.05;
glutPostRedisplay();
}
break;
case 'a':
{
col=5;
xg1-=0.05;
glutPostRedisplay();
}break;
case 's':
{
col=6;
yg1-=0.05;
glutPostRedisplay(); }
break;
case 'z':
{
col=7;
glutPostRedisplay();
}
break;
default:
break;
}
}
void MainMenuHandler(int option) //定义主菜单功能
{
switch(option)
{
case 0:
{
exit(0);
LoadGLTextures();
} break;
case 1:
{
col=1;
} break;
case 2:
{
col=2;
}break;
default:
break;
}
glutPostRedisplay();
}
void MenuHandler() // 主菜单
{
int mainMenu;
mainMenu = glutCreateMenu(MainMenuHandler);
glutSetMenu(mainMenu);
glutAddMenuEntry("Exit", 0);
glutAddMenuEntry("bright blue", 1);
glutAddMenuEntry("croci", 2);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
5.2 键盘控制火焰燃烧方向
(代码可参照键盘控制火焰颜色)
5.3 选择贴图纹理
用键盘输入选择纹理所对应的编号,代码可参照附录源代码中int LoadGLTextures()函数。
6、 程序运行结果
图6.1 图6.2
例如:选择2号:五角星火焰(图6.1) 贴图纹理:五角星(图6.2)
图6.3.1 图6.3.2
例如:选择4号:火焰(图6.3.1) 贴图纹理:火焰(图6.3.2)
图6.4.1 图6.4.2 图6.4.3
按键改变的火焰颜色(图6.4.1 图6.4.2 图6.4.3 )
图6.5.1 图6.5.2
鼠标主菜单,改变火焰颜色,和退出。例如选择“bright blue”,结果如图6.5.1 与图6.5.2
图6.6.1 图6.6.2
通过改变粒子X与Y轴的初速度和加速度,来改变火焰的燃烧方向,如图6.6.1与图6.6.2
7、总结
在完成基于粒子系统的火焰模拟实验的过程中,我们小组各成员通过分工与合作多种形式,查阅和检索相关文献及资料,最终通过学习基本原理、讨论实验方案、研究重点难点及验证预期结果等过程完成了本次实验。我们实验的特色在于使用键盘和鼠标控制火焰颜色的交互变化,以及使用键盘控制火焰加速度的变化。在本次实验中我们学习并掌握了相关知识,同时也深刻的体会到了合作的重要性。今后,我们一定会更加认真学习。
8、参考资料:
《计算机图形学基础(第2版)》 陆枫 何云峰 编著
太原工业学院精品课程
9、附录:源程序代码清单
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <Gl/glut.h>
#include <GL/glaux.h>
#include "math.h"
#define MAX_PARTICLES 5000 // 粒子数的数目
float slowdown=3.0f;
float xspeed; // X轴速度
float yspeed; // Y轴速度
float zoom=-40.0f;
int a;
float xg1;
float yg1=0.2f;
GLuint loop; // 循环变量
GLuint col=5; // 初始颜色
GLuint texture[1]; // 创建数组存储纹理
typedef struct // 创建结构体
{
bool active; //
float life; // 粒子生命周期
float fade; // 消失的速度
float r;
float g;
float b;
float x; // 坐标
float y; // 坐标
float z; // 坐标
float xi;
float yi;
float zi;
float xg; // X轴加速度
float yg; // Y轴加速度
float zg; // Z轴加速度
}
particles;
particles particle[MAX_PARTICLES]; // 创建粒子数组
static GLfloat colors[8][3]= // 颜色数组
{
{0.0f,0.0f,0.0f},{0.5f,0.75f,1.0f},{1.0f,0.75f,0.5f},{0.75f,1.0f,0.5f},
{1.0f,0.5f,0.75f}, {0.35f,0.8f,0.5f}, {0.55f,0.25f,0.75f},{0.5f,0.5f,0.75f},
};
AUX_RGBImageRec *LoadBMP(char *Filename) // 载入位图图像
{
FILE *File=NULL;
if (!Filename)
{
return NULL;
}
File=fopen(Filename,"r");
if (File)
{
fclose(File);
return auxDIBImageLoad(Filename);
}
return NULL;
}
int LoadGLTextures() // 载入位图(调用上面的代码)并转换成纹理
{
int Status=FALSE;
AUX_RGBImageRec *TextureImage[1];
memset(TextureImage,0,sizeof(void *)*1);
printf("q键 xg1+=0.05;(X轴加速度) 荧光黄色\n");
printf("w键 yg1+=0.05;(X轴加速度) 玫瑰粉\n");
printf("a键 xg1-=0.05;(X轴加速度) 荧光绿\n");
printf("s键 yg1-=0.05;(X轴加速度) 亮紫\n");
printf("z键 yg1-=0.05;(X轴加速度) 藕荷色\n");
printf("\n\n");
printf("***Choose the number of the shape***\n"); //选择纹理
printf("---1. Circle\t2. Pentagram\t3. Triangle---\n\n");
printf("---4. Flame\t5. four-pointed Star\t6. Hexagon---\n\n");
scanf("%d",&a);
if(a>6)
{
printf("****Tip:Error!!!Please re-enter after exit!!****\n\n");
exit(0);
}
switch(a)
{
case 1:
{
if (TextureImage[0]=LoadBMP("Circle.bmp"))
{
Status=TRUE;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
} break;
case 2:
{
if (TextureImage[0]=LoadBMP("Pentagram.bmp"))
{
Status=TRUE;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
}break;
case 3:
{
if (TextureImage[0]=LoadBMP("Triangle.bmp"))
{
Status=TRUE;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
}break;
case 4:
{
if (TextureImage[0]=LoadBMP("Flame.bmp"))
{
Status=TRUE;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
}break;
case 5:
{
if (TextureImage[0]=LoadBMP("four-pointed Star.bmp"))
{
Status=TRUE;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
}break;
case 6:
{
if (TextureImage[0]=LoadBMP("Hexagon.bmp"))
{
Status=TRUE;
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
}break;
}
if (TextureImage[0])
{
if (TextureImage[0]->data)
{
free(TextureImage[0]->data);
}
free(TextureImage[0]);
}
return Status;
}
void ReSizeGLScene(GLsizei width, GLsizei height) // 调整并初始化GL窗口
{
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,200.0f);
}
void InitGL(GLvoid) // OpenGL的设置
{
glClearColor(0.0f,0.0f,0.0f,0.0f);
glShadeModel(GL_SMOOTH);
glClearDepth(1.0f);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
glEnable(GL_TEXTURE_2D);
LoadGLTextures();
glBindTexture(GL_TEXTURE_2D,texture[0]);
for (loop=0;loop<MAX_PARTICLES;loop++)
{
particle[loop].active=true;
particle[loop].life=2.0f;
particle[loop].fade=float(rand()%100)/1500.0f+0.002f;
particle[loop].r=colors[loop*(4/MAX_PARTICLES)][0];
particle[loop].g=colors[loop*(4/MAX_PARTICLES)][1];
particle[loop].b=colors[loop*(4/MAX_PARTICLES)][2];
particle[loop].xg=0.0f;
particle[loop].yg=0.3f;
particle[loop].zg=0.0f;
}
}
void DrawGLScene(GLvoid) // 绘制
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0,0,5,
0,1.5,0,
0,1,0);
for (loop=0;loop<MAX_PARTICLES;loop++)
{
if (particle[loop].active)
{
float x=particle[loop].x;
float y=particle[loop].y;
float z=particle[loop].z+zoom;
glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z);
glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z);
glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z);
glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z);
glEnd();
particle[loop].x+=particle[loop].xi/(slowdown*1050); //X的坐标随X轴速度变化
particle[loop].y+=particle[loop].yi/(slowdown*1050);
particle[loop].z+=particle[loop].zi/(slowdown*1050);
particle[loop].xi+=particle[loop].xg; // X轴的速度随X轴的加速度变化
particle[loop].yi+=particle[loop].yg;
particle[loop].zi+=particle[loop].zg;
particle[loop].life-=particle[loop].fade; // 周期衰减
if (particle[loop].life<0.0f)
{
particle[loop].life=2.0f;
particle[loop].fade=float(rand()%100)/1500.0f+0.002f;
particle[loop].x=0.0f;
particle[loop].y=0.0f;
particle[loop].z=0.0f;
particle[loop].xi=float((rand()%60)-30.0f);
particle[loop].yi=float((rand()%60)-30.0f);
particle[loop].zi=float((rand()%60)-30.0f);
particle[loop].r=colors[col][0]; //渲染
particle[loop].g=colors[col][1]; //渲染
particle[loop].b=colors[col][2]; //渲染
particle[loop].xg=xg1;
particle[loop].yg=yg1;
particle[loop].zg=0.0f;
}
}
}
glutSwapBuffers();
}
void MainMenuHandler(int option) //定义主菜
展开阅读全文