今天要实现的是利用OpenGL模拟三个物理学中常见的运动:自由落体,匀速之间,弹簧振子。
首先要实现一个空间三维向量类,看这篇文章:向量类的实现
然后主要要实现的是质点类,
/*
Copyright: 2012, ustc All rights reserved.
contact:k283228391@126.com
File name: mass.h
Description:Particle in Physical
Author:Silang Quan
Version: 1.0
Date: 2012.12.21
*/
#ifndef MASS_H
#define MASS_H
#include "vector3d.h"
class Mass
{
public:
Mass();
Mass(float _m,float _size){m=_m;size=_size;};
void init();
void simulate(float dt);
void checkBnd(float x1,float x2,float y1,float y2);
void setForce(Vector3D _force){force=_force;};
void setVel(Vector3D _vel){vel=_vel;};
virtual ~Mass();
inline Vector3D getPos(){return pos;};
inline Vector3D getVel(){return vel;};
inline Vector3D getForce(){return force;};
inline float getSize(){return size;};
protected:
private:
float size; //大小
float m; // 质量
Vector3D pos; // 位置
Vector3D vel; // 速度
Vector3D force; // 力
};
#endif // MASS_H
简单解释一下:质点主要有大小,质量,速度,受力这四个属性。除了一些getset方法外,最关键的是simulate和checkBnd连个方法,分别是模拟运动和边界检测,实现起来也比较简单。
/*
Copyright: 2012, ustc All rights reserved.
contact:k283228391@126.com
File name: mass.cpp
Description:Particle in Physical
Author:Silang Quan
Version: 1.0
Date: 2012.12.21
*/
#include "mass.h"
Mass::Mass()
{
//ctor
}
void Mass::init()
{
force=Vector3D::zero();
vel=Vector3D::zero();
}
void Mass::simulate(float dt)
{
Vector3D acl=force/m;
vel=vel+acl*dt;
pos=pos+vel*dt;
}
Mass::~Mass()
{
//dtor
}
void Mass::checkBnd(float x1,float x2,float y1,float y2)
{
if (pos.x - size < x1 || pos.x +size > x2) vel.x = -vel.x;
if (pos.y - size < y1 || pos.y + size > y2) vel.y = -vel.y;
}
在simulate函数中我们用到了运动学的公式:a=F/m,Vt=Vo+at.deltaS=at^2.
接下来我们就可以来进行模拟了!
首先在main.cpp中声明几个全局变量:
GLUquadricObj *mySphere;
Mass constVelBall;
Mass gravityBall;
Mass springBall;
GLUquadricObj是二次曲面对象的声明类,后面三个是声明三个质点。
接下来是对三个质点的初始化:
void initMasses()
{
constVelBall=Mass(5.0,3);
constVelBall.init();
constVelBall.setVel(Vector3D(3.0,5.0,0.0));
gravityBall=Mass(5.0,2);
gravityBall.init();
gravityBall.setVel(Vector3D(-1.0,0.0,0.0));
gravityBall.setForce(Vector3D(0.0,-2.0,0.0));
springBall=Mass(5.0,4);
springBall.init();
springBall.setVel(Vector3D(2.0,0.0,2.0));
}
然后是模拟检测:
void simulateCheck()
{
Vector3D connectionPos = Vector3D(0.0f, 0.0f, 0.0f);
constVelBall.simulate(0.1);
constVelBall.checkBnd(-20,20,-20,20);
gravityBall.simulate(0.1);
gravityBall.checkBnd(-20,20,-20,20);
springBall.simulate(0.1);
Vector3D springVector = springBall.getPos() - connectionPos;
springBall.setForce(springVector*(-0.1));
}
这两个函数在main中进行调用。
还需要添加一个绘制质点的函数。我们用球体来代表质点,Mass中的size域就用来做半径。
void drawMass(Mass m)
{
Vector3D tmp=m.getPos();
glPushMatrix();
glTranslatef(tmp.x,tmp.y,tmp.z);
gluSphere(mySphere,m.getSize(), 32, 16);
glPopMatrix();
}
最后来看渲染函数:
void renderGL()
{
// Clear the color and depth buffers.
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// We don't want to modify the projection matrix. */
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
// Move down the z-axis.
glTranslatef(0.0f,0.0f,-35.0f);
glColor3ub(0, 0, 255); // Draw In Blue
glBegin(GL_LINES);
// Draw The Vertical Lines
for (float x = -20; x <= 20; x += 2.0f) // x += 1.0f Stands For 1 Meter Of Space In This Example
{
glVertex3f(x, 20, 0);
glVertex3f(x,-20, 0);
}
// Draw The Horizontal Lines
for (float y = -20; y <= 20; y += 2.0f) // y += 1.0f Stands For 1 Meter Of Space In This Example
{
glVertex3f( 20, y, 0);
glVertex3f(-20, y, 0);
}
glEnd();
glColor3ub(0,255, 255);
drawMass(constVelBall);
glColor3ub(255,0, 255);
drawMass(gravityBall);
glColor3ub(255,255,0);
drawMass(springBall);
SDL_GL_SwapBuffers( );
}
先渲染一些网格,然后对质点进行渲染。
最后的效果就像这样: