其实是我的图形学作业。火焰生成的算法写的不太好,蔓延得也不算真实。唯一的优点大概就是速度比较快吧,因为算法较简单。 效果: 贴一下C++源代码。仅供参考,工程里的其他内容和图片都没有放上来。 #include <windows.h> #include <math.h> #include <time.h> #include <GL/glaux.h> #include <GL/glut.h> #include <stdio.h> float angle=0.0,deltaAngle = 0.0,ratio; float x=0.0f,y=1.2f,z=15.0f; float lx=0.0f,ly=-0.2f,lz=-1.0f; int deltaMove = 0; time_t time_now,time_last; float radius = 0.1; int aa = 0; #define NUM_TREE_TEXTURES 4 GLint tree_display_list[NUM_TREE_TEXTURES]; #define NUM_TEXTURES 10 int count = -1; GLuint tobjects[NUM_TEXTURES]; #define NUM_TREES 50 struct Tree{ double x, //x coordinate y; //y coordinate int type; //decide the type from tree_display_list }tree[NUM_TREES]; /***********************************粒子系统***********************************************************************************/ #define MAX_PARTICLES 100000 // 定义最大的粒子数 float slowdown=2.0f; // 减速粒子 float init_x,init_z; GLuint loop,now_particles = 50,tmp_particles; typedef struct // 创建粒子数据结构 { float life; // 粒子生命 float fade; // 衰减速度 float x; // X 位置 float y; // Y 位置 float z; // Z 位置 float xi; // X 方向 float yi; // Y 方向 float zi; // Z 方向 float xg; // X 方向重力加速度 float yg; // Y 方向重力加速度 float zg; // Z 方向重力加速度 } particles; // 粒子数据结构 particles particle[MAX_PARTICLES]; // 保存MAX_PARTICLES个粒子的数组 /********************************************************************************************************************************/ AUX_RGBImageRec *LoadBMP(const char *Filename) // 载入位图文件 { FILE *File=NULL; // 文件句柄 if (!Filename) // 确认已给出文件名 { return NULL; // 若无返回 NULL } File=fopen(Filename,"r"); // 检查文件是否存在 if (File) // 文件存在么? { fclose(File); // 关闭文件句柄 return auxDIBImageLoad(/*(LPCWSTR)*/Filename); // 载入位图并返回指针 } return NULL; // 如果载入失败返回 NULL } int LoadGLTextures(const char *Filename) // 载入位图并转换成纹理 { int Status=FALSE; // 状态指示器 BYTE texture[65536][4]; AUX_RGBImageRec *TextureImage[1]; // 为纹理分配存储空间 memset(TextureImage,0,sizeof(void *)*1); // 将指针设为 NULL // 载入位图,查错,如果未找到位图文件则退出 if (TextureImage[0]=LoadBMP(Filename)) { Status=TRUE; // 将 Status 设为TRUE if (count >= 0 && count <= 3) for (int i=0;i<TextureImage[0]->sizeX*TextureImage[0]->sizeY;i++) { texture[i][0]=TextureImage[0]->data[i*3]; texture[i][1]=TextureImage[0]->data[i*3+1]; texture[i][2]=TextureImage[0]->data[i*3+2]; if (!texture[i][0] && !texture[i][1] && !texture[i][2]) texture[i][3]=0; else texture[i][3]=255; } else if (count == 4) for (int i=0;i<TextureImage[0]->sizeX*TextureImage[0]->sizeY;i++) { texture[i][0]=TextureImage[0]->data[i*3]; texture[i][1]=TextureImage[0]->data[i*3+1]; texture[i][2]=TextureImage[0]->data[i*3+2]; texture[i][3]=texture[i][0]; } glGenTextures(1, &tobjects[++count]); // 创建一个纹理 // 创建一个线性滤波纹理 glBindTexture(GL_TEXTURE_2D, tobjects[count]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); if (count ==0 || count >5) glTexImage2D(GL_TEXTURE_2D, 0, 4, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); else glTexImage2D(GL_TEXTURE_2D, 0, 4, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture); } if (TextureImage[0]) // 如果纹理存在 { if (TextureImage[0]->data) // 如果纹理图像存在 { free(TextureImage[0]->data); // 释放纹理图像所占的内存 } free(TextureImage[0]); // 释放图像结构 } return Status; // 返回 Status的值 } void changeSize(int w, int h) { // Prevent a divide by zero, when window is too short if(h == 0) h = 1; ratio = 1.0f * w / h; // Reset the coordinate system before modifying glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Set the viewport to be the entire window glViewport(0, 0, w, h); // Set the clipping volume gluPerspective(45,ratio,1,1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(x, y, z, x + lx,y + ly,z + lz, 0.0f,1.0f,0.0f); } void drawTree(int i) { glColor3f(1.0f, 1.0f, 1.0f); // Load the tree texture switch (i) { case 0: LoadGLTextures("Tree1.BMP"); break; case 1: LoadGLTextures("Tree2.BMP"); break; case 2: LoadGLTextures("Tree3.BMP"); break; case 3: LoadGLTextures("Tree4.BMP"); break; default: break; } // Draw the Tree glBindTexture(GL_TEXTURE_2D, tobjects[i+1]); glBegin(GL_QUADS); glTexCoord2f(1.0f, 1.0f); glVertex3i(1, 2, 0); glTexCoord2f(1.0f, 0.0f); glVertex3i(1, 0, 0); glTexCoord2f(0.0f, 0.0f); glVertex3i(-1, 0, 0); glTexCoord2f(0.0f, 1.0f); glVertex3i(-1, 2, 0); glEnd(); } GLuint createDL(int i) { GLuint TreeDL; // Create the id for the list TreeDL = glGenLists(1); // start list glNewList(TreeDL,GL_COMPILE); // call the function that contains // the rendering commands drawTree(i); // endList glEndList(); return(TreeDL); } void initScene() { int i; glClearColor(0.53f, 0.808f, 0.921f, 1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); glEnable(GL_POINT_SMOOTH); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //读取地面的图片 LoadGLTextures("Ground.BMP"); // Create the trees for (i = 0; i < 4; i ++) tree_display_list[i] = createDL(i); for (i = 0; i< NUM_TREES; i ++) { tree[i].x =double(rand() % 200) / 10 - 10; tree[i].y = double(rand() % 200) / 10 - 10; tree[i].type = rand() % NUM_TREE_TEXTURES; } init_x = double(rand() % 8000) / 1000 - 4; init_z = double(rand() % 8000) / 1000 - 4; LoadGLTextures("Particle.bmp"); for (loop = 0;loop < now_particles;loop ++) //初始化所有的粒子 { particle[loop].life=1.0f; // 所有的粒子生命值为最大 particle[loop].fade=float(rand()%90)/1000.0f+0.013f; // 随机生成衰减速率 particle[loop].x=init_x + float(rand() % (int(radius*1000))) / 1000; particle[loop].y=0.0f; particle[loop].z=init_z + float(rand() % (int(radius*1000))) / 1000; particle[loop].xi=float((rand()%70)-32.0f); // 随机生成粒子速度 particle[loop].yi=float((rand()%70)-30.0f); particle[loop].zi=float((rand()%70)-30.0f); particle[loop].xg=0.0f; // 设置X轴方向加速度为0 particle[loop].yg=1.2f; // 设置Y轴方向加速度为1.2 particle[loop].zg=0.0f; // 设置Z轴方向加速度为0 } /********************************************************************************************************************************/ // glDepthFunc(GL_LEQUAL); // 所作深度测试的类型 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 告诉系统对透视进行修正 //读取天空的图片 LoadGLTextures("Front.bmp"); LoadGLTextures("Left.bmp"); LoadGLTextures("Back.bmp"); LoadGLTextures("Right.bmp"); } void orientMe(float ang) { lx = sin(ang); lz = -cos(ang); glLoadIdentity(); gluLookAt(x, y, z, x + lx,y + ly,z + lz, 0.0f,1.0f,0.0f); } void moveMeFlat(int i) { x = x + i*(lx)*0.1; z = z + i*(lz)*0.1; glLoadIdentity(); gluLookAt(x, y, z, x + lx,y + ly,z + lz, 0.0f,1.0f,0.0f); } void renderScene(void) { int i; if (deltaMove) moveMeFlat(deltaMove); if (deltaAngle) { angle += deltaAngle; orientMe(angle); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //画地面 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(GL_TEXTURE_2D, tobjects[0]); glBegin(GL_QUADS); glNormal3i(0, 1, 0); glTexCoord2f(20.0f, 20.0f); glVertex3i(50, 0, -50); glTexCoord2f(20.0f, 0.0f); glVertex3i(-50,0,-50); glTexCoord2f(0.0f, 0.0f); glVertex3i(-50,0,50); glTexCoord2f(0.0f, 20.0f); glVertex3i(50, 0, 50); glEnd(); //画天空 glBindTexture(GL_TEXTURE_2D, tobjects[6]); glBegin(GL_QUADS); glNormal3i(0, 1, 0); glTexCoord2f(1.0f, 1.0f); glVertex3i(-100, 100, -100); glTexCoord2f(1.0f, 0.0f); glVertex3i(-100,-40,-100); glTexCoord2f(0.0f, 0.0f); glVertex3i(100,-40,-100); glTexCoord2f(0.0f, 1.0f); glVertex3i(100, 100, -100); glEnd(); glBindTexture(GL_TEXTURE_2D, tobjects[7]); glBegin(GL_QUADS); glNormal3i(0, 1, 0); glTexCoord2f(1.0f, 1.0f); glVertex3i(-100, 100, 100); glTexCoord2f(1.0f, 0.0f); glVertex3i(-100,-40,100); glTexCoord2f(0.0f, 0.0f); glVertex3i(-100,-40,-100); glTexCoord2f(0.0f, 1.0f); glVertex3i(-100, 100, -100); glEnd(); glBindTexture(GL_TEXTURE_2D, tobjects[8]); glBegin(GL_QUADS); glNormal3i(0, 1, 0); glTexCoord2f(1.0f, 1.0f); glVertex3i(100, 100, 100); glTexCoord2f(1.0f, 0.0f); glVertex3i(100,-40,100); glTexCoord2f(0.0f, 0.0f); glVertex3i(-100,-40, 100); glTexCoord2f(0.0f, 1.0f); glVertex3i(-100, 100, 100); glEnd(); glBindTexture(GL_TEXTURE_2D, tobjects[9]); glBegin(GL_QUADS); glNormal3i(0, 1, 0); glTexCoord2f(1.0f, 1.0f); glVertex3i(100, 100, -100); glTexCoord2f(1.0f, 0.0f); glVertex3i(100,-40,-100); glTexCoord2f(0.0f, 0.0f); glVertex3i(100,-40,100); glTexCoord2f(0.0f, 1.0f); glVertex3i(100, 100, 100); glEnd(); for(i = 0; i < NUM_TREES; i ++) { glPushMatrix(); glTranslatef(tree[i].x, 0, tree[i].y); glRotatef(360-angle*180/3.1415926, 0.0, 1.0, 0.0); glCallList(tree_display_list[tree[i].type]); glPopMatrix(); } time_now = time(0); if (time_now - time_last >= 2) { radius += 0.1; time_last = time_now; } glEnable(GL_BLEND); glDepthMask(GL_FALSE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); glBindTexture(GL_TEXTURE_2D, tobjects[5]); tmp_particles = now_particles; for (loop=0;loop<now_particles;loop++) { float x=particle[loop].x; // 返回X轴的位置 float y=particle[loop].y; // 返回Y轴的位置 float z=particle[loop].z; // 返回Z轴的位置 // 设置粒子颜色 glColor4f(1.0f,0.5f,0.0f,particle[loop].life); glPushMatrix(); glTranslatef(x, y, z); glRotatef(360-angle*180/3.1415926, 0.0, 1.0, 0.0); glBegin(GL_TRIANGLE_STRIP); // 绘制三角形带 glTexCoord2d(1,1); glVertex3f(0.5f,0.5f,0); glTexCoord2d(0,1); glVertex3f(-0.5f,0.5f,0); glTexCoord2d(1,0); glVertex3f(0.5f,-0.5f,0); glTexCoord2d(0,0); glVertex3f(-0.5f,-0.5f,0); //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(); glPopMatrix(); //particle[loop].x+=particle[loop].xi/(slowdown*1000); // 更新X坐标的位置 particle[loop].y+=particle[loop].yi/(slowdown*1000); // 更新Y坐标的位置 //particle[loop].z+=particle[loop].zi/(slowdown*1000); // 更新Z坐标的位置 //particle[loop].xi+=particle[loop].xg; // 更新X轴方向速度大小 particle[loop].yi+=particle[loop].yg; // 更新Y轴方向速度大小 //particle[loop].zi+=particle[loop].zg; // 更新Z轴方向速度大小 particle[loop].life-=particle[loop].fade; // 减少粒子的生命值 if (particle[loop].life<0.0f) // 如果粒子生命值小于0 { particle[loop].life=1.0f; // 产生一个新的粒子 particle[loop].fade=float(rand()%90)/1000.0f+0.013f; // 随机生成衰减速率 particle[loop].x=init_x + float(rand() % (int(radius*1000))) / 1000; particle[loop].y=0.0f; particle[loop].z=init_z + float(rand() % (int(radius*1000))) / 1000; particle[loop].xi=float((rand()%70)-32.0f); // 随机生成粒子速度 particle[loop].yi=float((rand()%70)-30.0f); particle[loop].zi=float((rand()%70)-30.0f); int tmp = (rand() % 40); //每个死亡粒子有机会产生两个新的粒子,这样火焰可以蔓延 if (tmp == 1) { tmp_particles ++; particle[tmp_particles].life=1.0f; particle[tmp_particles].fade=float(rand()%90)/1000.0f+0.013f; particle[tmp_particles].x=init_x + float(rand() % (int(radius*1000))) / 1000; particle[tmp_particles].y=0.0f; particle[tmp_particles].z=init_z + float(rand() % (int(radius*1000))) / 1000; particle[tmp_particles].xi=float((rand()%70)-32.0f); particle[tmp_particles].yi=float((rand()%70)-30.0f); particle[tmp_particles].zi=float((rand()%70)-30.0f); } } } now_particles = tmp_particles; glDepthMask(GL_TRUE); glDisable(GL_BLEND); glFlush(); glutSwapBuffers(); } void pressKey(int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT : deltaAngle = -0.01f;break; case GLUT_KEY_RIGHT : deltaAngle = 0.01f;break; case GLUT_KEY_UP : deltaMove = 1;break; case GLUT_KEY_DOWN : deltaMove = -1;break; } } void releaseKey(int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT : case GLUT_KEY_RIGHT : deltaAngle = 0.0f;break; case GLUT_KEY_UP : case GLUT_KEY_DOWN : deltaMove = 0;break; } } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(10,10); glutInitWindowSize(640,360); glutCreateWindow("Forrest Fire"); srand((unsigned)time(0)); time_now = time(0); time_last = time_now; initScene(); glutIgnoreKeyRepeat(1); glutSpecialFunc(pressKey); glutSpecialUpFunc(releaseKey); glutDisplayFunc(renderScene); glutIdleFunc(renderScene); glutReshapeFunc(changeSize); glutMainLoop(); return(0); }