OpenGL进阶(五):物理模拟
发表于2017-09-13
今天要实现的是利用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));
- }
然后是模拟检测: copy
- 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( );
- }
先渲染一些网格,然后对质点进行渲染。
最后的效果就像这样: