OpenGL绘制球体模拟自由落体运动(基于Qt)

自由落体的物理公式想必大家都清楚(y=vt+0.5gt^2)。

但是用程序如何模拟这个过程呢?

1、其中比较关键的是设定一个计时器,在一个视觉暂留时间段(dt=0.02s)内根据小球的位置坐标绘制小球。

2、在小球弹到地面的时候会因为碰撞而有能量损耗,其表现形式为速度减小并反向,而速度减小则可以通过改变加速度a的大小来控制(F=ma嘛,不知道这样解释合不合理,迷)

3、终止条件的判断:

    if(ball.vy<1 && ball.y<-2.0){       //中止条件
        ball.vy=0;
        ball.y=-2.0;
        G=0;
}

这里ball.vy为速度大小,ball.y为高度坐标(地面位置为-2.0),条件为什么不是ball.vy<0呢?这是因为速度的减少或增加是以dt为单位的(非连续),不一定能取到0值,如果条件设为0那么小球会一直在一个很小的范围内震动。(不信你可以试试)


下面附上我的程序实现:(因为考试复习原因没有给小球添加纹理,后面有时间了再补上)

widget.h

QTimer *timer;  //定时器
    //GLuint texName; //材质路径
    GLdouble dt;    //间隔时间
    bool direction; //小球运动方向,向下为true
    double G;
    GLuint *tex0;//纹理
    QString bmpfile;
    QPoint m_last;
    double dist,horizontal,vertical;
 
  
    typedef struct b //定义储存球体的结构
    {
        GLdouble y;
        GLdouble vy;
    } Ball;
    Ball ball;
 
  
public slots:
    void updataScene();

widget.cpp

Widget::Widget(QWidget *parent)
    : QGLWidget(parent)
{
    timer = new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(updataScene()));
    timer->start(10);
}
void Widget::initializeGL(){
    dt=0.02;
    ball.y = 10 ; //初始化球体属性
    ball.vy = 0 ;
    direction = true;
    G = 9.8;
    bmpfile = "://floor.jpg";
    tex0 = new GLuint;
    dist = 2.0;
    vertical=45.0;
    horizontal=45.0;
        glClearColor(0.0,0.0,0.0,0.0);
        glEnable(GL_DEPTH_TEST);
        glShadeModel(GL_SMOOTH);
        //glEnable(GL_LIGHTING); //开启光照
        // glEnable(GL_LIGHT0); //开启光源0
         glEnable(GL_AUTO_NORMAL);
         glEnable(GL_NORMALIZE);
         glMaterialf(GL_FRONT,GL_SHININESS,64.0);
 
  
         loadTexture(bmpfile,tex0);
}
void Widget::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
  
    glClearColor(0.6,0.6,0.6,1);
    glLoadIdentity();
 
  
    float eyex=dist*cos(vertical/180.0*PI)*sin(horizontal/180.0*PI);
    float eyey=dist*sin(vertical/180.0*PI);
    float eyez=dist*cos(vertical/180.0*PI)*cos(horizontal/180.0*PI);
    gluLookAt(eyex, eyey,eyez, 0.0, 0.0, 0.0, 0, 1, 0);
    //gluLookAt(1,2,1,0,0,0,0,1,0);
 
  
    glDisable(GL_TEXTURE_2D);   //画小球时先不使用纹理
    glPushMatrix();
    glTranslated(0,ball.y,0.0);
    glutWireSphere(2,50,50);
 
  
    if(ball.vy<1 && ball.y<-2.0){       //中止条件
        ball.vy=0;
        ball.y=-2.0;
        G=0;
    }
    if(direction){  //向下运动
        G=9.8;
        if(ball.y-(ball.vy*dt+0.5*G*dt*dt) <=-2){
            direction = false; //触发转向条件,改变direction的值,使球体运动方向改变
                        G += 6.0;
        }
        ball.y = ball.y-(ball.vy*dt+0.5*G*dt*dt); //根据牛顿运动定律计算出球的位移公式
        ball.vy = ball.vy + G*dt; //根据牛顿运动定律计算出球体的速度
    }
    else{   //向上运动
        if(ball.y >-2 && ball.vy<0){
            direction = true;
        }
        ball.y = ball.y+(ball.vy*dt-0.5*G*dt*dt);
        ball.vy = ball.vy-G*dt;
 
  
    }
        glPopMatrix(); //将当前矩阵弹出
 
  
        glPushMatrix();
       // glPushAttrib(GL_ALL_ATTRIB_BITS);
        // glColor3f(0.0, 0.0, 1.0);
        glEnable(GL_TEXTURE_2D);
       // loadTexture(bmpfile,tex0);
         glBegin(GL_QUADS);
         glTexCoord2f( 0.0, 0.0 );glVertex3f(10.0,-4.0,0.0);
         glTexCoord2f( 1.0, 0.0 );glVertex3f(0.0,-4.0,-6.0);
         glTexCoord2f( 1.0, 1.0 );glVertex3f(-6.0,-4.0,0.0);
         glTexCoord2f( 0.0, 1.0 );glVertex3f(0.0, -4.0,10.0);
         glEnd();
         //glPopAttrib();
       glPopMatrix();
 
  
        glFlush();
}
void Widget::loadTexture(QString filepath, GLuint *texture){    //加载纹理
            QImage tex, buf;
            if(!buf.load(filepath))
            {
                qDebug()<<"Error: failed to load image!";
                exit(1);
            }
            tex = convertToGLFormat(buf);   //转化为rgba类型的数据集
            glGenTextures(1, texture);
            glBindTexture(GL_TEXTURE_2D, *texture);
            gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, tex.width(), tex.height(), GL_RGBA, GL_UNSIGNED_BYTE,tex.bits());
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,3);
}
void Widget::mousePressEvent(QMouseEvent *e)
{
    m_last = e->pos();
}
void Widget::mouseMoveEvent(QMouseEvent *e)
{
    if(e->buttons() & Qt::LeftButton)
    {
        QPoint m_now = e->pos();
        float disx = m_now.x() - m_last.x();//两个鼠标x坐标的差
        float disy = m_now.y() - m_last.y();  //m_last是之前点击事件取得的鼠标位置
        //更改观察方向
        horizontal -= disx/25;
        vertical += disy/25;
        m_last = m_now;
        updateGL();
    }
}

运行结果截图:

OpenGL绘制球体模拟自由落体运动(基于Qt)_第1张图片

此处只截取从一定高度落下并来回弹跳的最终结果。以下为完整工程代码的链接:

https://download.csdn.net/download/did_you/10406118


你可能感兴趣的:(qt)