OpenGL动画场景中鼠标触发贝塞尔烟花绘制失效求助
解决GLUT动画窗口中鼠标触发烟花无法持久显示的问题
我明白你的问题了——静态窗口下烟花能正常画出来,但在持续刷新的动画窗口里,刚点出来的烟花瞬间就被清掉了。这是因为你的display函数每次都会用glClear清空整个缓冲区,而你在mousePressed里直接绘制的烟花只存在于当前帧,下一次刷新(因为glutIdleFunc(display)持续调用)就被覆盖了。
下面是具体的解决方案,核心思路是把烟花的状态保存下来,让display函数每次刷新时都重新绘制所有烟花:
步骤1:定义烟花数据结构
首先我们需要一个结构体来存储每个烟花的贝塞尔曲线参数,这样就能在每次刷新时重新绘制它:
struct Firework { int xi, yi, xf, yf; int x1b, y1b, x2b, y2b; // 可选:添加生命周期,控制烟花显示时长 float lifespan; bool active; };
步骤2:存储所有烟花实例
用一个动态数组(比如std::vector)来保存所有需要绘制的烟花:
#include <vector> // 需要包含这个头文件 std::vector<Firework> fireworks;
步骤3:修改鼠标事件处理函数
不再在鼠标事件里直接绘制烟花,而是生成烟花的参数并添加到数组中:
void mousePressed(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { xi = x; yi = wh - y; xf = x + 5.; Firework fw; fw.xi = xi; fw.yi = yi; fw.xf = xf; fw.yf = yi; // 生成贝塞尔中间控制点(和你原来的逻辑一致) fw.x1b = xi + rand()%15; fw.y1b = yi + rand()%5; fw.x2b = xf + rand()%15; fw.y2b = yf + rand()%5; // 修正原代码中误用xf的笔误 fw.lifespan = 2.0; // 让烟花显示2秒 fw.active = true; fireworks.push_back(fw); } glutPostRedisplay(); }
步骤4:修改display函数,统一绘制所有烟花
在display函数里,遍历烟花数组,对每个活跃的烟花调用贝塞尔绘制逻辑,同时更新它们的生命周期:
// 把原来的bezier函数改成接收Firework参数的版本 void drawBezier(const Firework& fw) { float px, py, t; glPointSize(2); glBegin(GL_POINTS); glColor3f(1,0,0); // 给烟花加上红色,原代码注释掉了这部分 for (t=0.;t<=1.;t+=0.001) { px=(1-t)*(1-t)*(1-t)*fw.xi+3*t*(1-t)*(1-t)*fw.x1b+3*t*t*(1-t)*fw.x2b+t*t*t*fw.xf; py=(1-t)*(1-t)*(1-t)*fw.yi+3*t*(1-t)*(1-t)*fw.y1b+3*t*t*(1-t)*fw.y2b+t*t*t*fw.yf; glVertex2d(px,py); } glEnd(); } void display() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // 先绘制你的动画元素(云、地面) glPushMatrix(); glTranslatef(x1,y,-5.0); glBegin(GL_POLYGON); glColor3f(1.,0.,0.5); glVertex3f(-1.,1.,-5.); glVertex3f(0.,2.,-5.); glVertex3f(-2.,2.,-5.); glVertex3f(1.,1.,-5.); glEnd(); glPopMatrix(); glPushMatrix(); glTranslatef(x,y,-5.); glBegin(GL_POLYGON); glColor3f(0.,0.5,0.5); glVertex3f(1.,0.7,-5.); glVertex3f(1.5,1.0,-5.0); glVertex3f(0.7,1.5,-5.0); glVertex3f(0.0,2.0,-5.0); glVertex3f(-0.7,1.5,-5.0); glVertex3f(-1.4,1.6,-5.0); glVertex3f(-1.7,1.0,-5.0); glVertex3f(-1.5,0.7,-5.0); glVertex3f(-1.0,0.5,-5.0); glEnd(); glPopMatrix(); glBegin(GL_POLYGON); glColor3f(1.,1.,1.5); glVertex3f(-2.,-2.,-5.); glVertex3f(-2.0,-2.0,-5.0); glVertex3f(-1.0,-1.5,-5.0); glVertex3f(2.0,-2.0,-5.0); glVertex3f(1.2,-1.5,-5.0); glEnd(); // 绘制所有活跃的烟花 for (auto it = fireworks.begin(); it != fireworks.end(); ) { if (it->active) { drawBezier(*it); // 更新生命周期,和你的update函数节奏匹配 it->lifespan -= 0.01; if (it->lifespan <= 0) { it->active = false; it = fireworks.erase(it); // 移除过期的烟花,避免内存占用 continue; } } ++it; } update(); glutSwapBuffers(); glFlush(); }
步骤5:补充细节优化
- 在
initRendering里添加srand(time(NULL)),初始化随机数种子,让烟花的控制点更随机自然。 - 确保包含
<cstdlib>头文件,支持rand()和srand()函数。
完整修改后的代码
#include<GL/glut.h> #include <vector> #include <cstdlib> #include <ctime> struct Point { GLint x; GLint y; }; Point p1, p2; int ww=600,wh=400; int xi,yi,xf,yf; float px, py, t; float x=0.,y=0.,x1=5.; // 定义烟花结构体 struct Firework { int xi, yi, xf, yf; int x1b, y1b, x2b, y2b; float lifespan; bool active; }; std::vector<Firework> fireworks; void update() { x+=0.01; x1 -= 0.02; if (x>6) { x -= 6; x1 = 4; } } // 绘制单个贝塞尔烟花 void drawBezier(const Firework& fw) { float px, py, t; glPointSize(2); glBegin(GL_POINTS); glColor3f(1,0,0); // 红色烟花 for (t=0.;t<=1.;t+=0.001) { px=(1-t)*(1-t)*(1-t)*fw.xi+3*t*(1-t)*(1-t)*fw.x1b+3*t*t*(1-t)*fw.x2b+t*t*t*fw.xf; py=(1-t)*(1-t)*(1-t)*fw.yi+3*t*(1-t)*(1-t)*fw.y1b+3*t*t*(1-t)*fw.y2b+t*t*t*fw.yf; glVertex2d(px,py); } glEnd(); } void initRendering() { glEnable(GL_DEPTH_TEST); srand(time(NULL)); // 初始化随机数种子 } void reshaped(int w , int h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, (double)w/(double)h,1,200); } void keyPressed(unsigned char k, int x, int y) { if(k==27) { exit(0); } } void mousePressed(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { xi=x; yi=wh-y; xf=x + 5.; Firework fw; fw.xi = xi; fw.yi = yi; fw.xf = xf; fw.yf = yi; fw.x1b = xi + rand()%15; fw.y1b = yi + rand()%5; fw.x2b = xf + rand()%15; fw.y2b = yf + rand()%5; fw.lifespan = 2.0; fw.active = true; fireworks.push_back(fw); } glutPostRedisplay(); } void display() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // 绘制移动的云 glPushMatrix(); glTranslatef(x1,y,-5.0); glBegin(GL_POLYGON); glColor3f(1.,0.,0.5); glVertex3f(-1.,1.,-5.); glVertex3f(0.,2.,-5.); glVertex3f(-2.,2.,-5.); glVertex3f(1.,1.,-5.); glEnd(); glPopMatrix(); glPushMatrix(); glTranslatef(x,y,-5.); glBegin(GL_POLYGON); glColor3f(0.,0.5,0.5); glVertex3f(1.,0.7,-5.); glVertex3f(1.5,1.0,-5.0); glVertex3f(0.7,1.5,-5.0); glVertex3f(0.0,2.0,-5.0); glVertex3f(-0.7,1.5,-5.0); glVertex3f(-1.4,1.6,-5.0); glVertex3f(-1.7,1.0,-5.0); glVertex3f(-1.5,0.7,-5.0); glVertex3f(-1.0,0.5,-5.0); glEnd(); glPopMatrix(); // 绘制地面 glBegin(GL_POLYGON); glColor3f(1.,1.,1.5); glVertex3f(-2.,-2.,-5.); glVertex3f(-2.0,-2.0,-5.0); glVertex3f(-1.0,-1.5,-5.0); glVertex3f(2.0,-2.0,-5.0); glVertex3f(1.2,-1.5,-5.0); glEnd(); // 绘制所有烟花 for (auto it = fireworks.begin(); it != fireworks.end(); ) { if (it->active) { drawBezier(*it); it->lifespan -= 0.01; if (it->lifespan <= 0) { it = fireworks.erase(it); continue; } } ++it; } update(); glutSwapBuffers(); glFlush(); } void myinit() { glViewport(0,0,ww,wh); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluOrtho2D(0.0,(GLdouble)ww,0.0,(GLdouble)wh); glMatrixMode(GL_MODELVIEW); } int main(int argc,char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowPosition(100,100); glutInitWindowSize(400,400); glutCreateWindow("Salute | Clouds"); initRendering(); glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(reshaped); glutKeyboardFunc(keyPressed); glutMouseFunc(mousePressed); glutMainLoop(); return(0); }
现在运行修改后的代码,点击鼠标左键就能看到持久显示的烟花了,而且烟花会在2秒后自动消失,避免画面堆积。
内容的提问来源于stack exchange,提问作者Bogdan Galay
相关产品推荐
相关产品推荐

