自由落体的物理公式想必大家都清楚(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();
}
}
运行结果截图:
此处只截取从一定高度落下并来回弹跳的最终结果。以下为完整工程代码的链接:
https://download.csdn.net/download/did_you/10406118