You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:56:15