当前位置: 首页 > 编程日记 > 正文

NeHe OpenGL第三十九课:物理模拟

NeHe OpenGL第三十九课:物理模拟

物理模拟简介:

还记得高中的物理吧,直线运动,自由落体运动,弹簧。在这一课里,我们将创造这一切。
 
物理模拟介绍

如果你很熟悉物理规律,并且想实现它,这篇文章很适合你。

在这篇教程里,你会创建一个非常简单的物理引擎,我们将创建以下类:

内容:

位置类
* class Vector3D --->  用来记录物体的三维坐标的类

力和运动
* class Mass --->  表示一个物体的物理属性

模拟类
* class Simulation --->  模拟物理规律

模拟匀速运动
* class ConstantVelocity : public Simulation --->  模拟匀速运动

模拟在力的作用下运动
* class MotionUnderGravitation : public Simulation --->  模拟在引力的作用下运动
* class MassConnectedWithSpring : public Simulation --->  模拟在弹簧的作用下运动

class Mass
{
public:
 float m;         // 质量
 Vector3D pos;        // 位置
 Vector3D vel;        // 速度
 Vector3D force;        // 力

 Mass(float m)        // 构造函数
 {
  this->m = m;
 }

 ...
 
 下面的代码给物体增加一个力,在初始时这个力为0

 void applyForce(Vector3D force)
 {
  this->force += force;      // 增加一个力
 }

 void init()        // 初始时设为0
 {
  force.x = 0;
  force.y = 0;
  force.z = 0;
 }

 ...

下面的步骤完成一个模拟:

1.设置力
2.应用外力
3.根据力的时间,计算物体的位置和速度

 void simulate(float dt)
 {
  vel += (force / m) * dt;     // 更新速度
        

  pos += vel * dt;      // 更新位置
          
 }

模拟类怎样运作:

在一个物理模拟中,我们按以下规律进行模拟,设置力,更新物体的位置和速度,按时间一次又一次的进行模拟。下面是它的实现代码: 
  
class Simulation
{
public:
 int numOfMasses;        // 物体的个数
 Mass** masses;        // 指向物体结构的指针

 Simulation(int numOfMasses, float m)      // 构造函数
 {
  this->numOfMasses = numOfMasses;

  masses = new Mass*[numOfMasses];    

  for (int a = 0; a < numOfMasses; ++a)    
   masses[a] = new Mass(m);   
 }

 virtual void release()       // 释放所有的物体
 {
  for (int a = 0; a < numOfMasses; ++a)    
  {
   delete(masses[a]);
   masses[a] = NULL;
  }

  delete(masses);
  masses = NULL;
 }

 Mass* getMass(int index)
 {
  if (index < 0 || index >= numOfMasses)    // 返回第i个物体
   return NULL;      

  return masses[index];      
 }

...

 (class Simulation continued)

 virtual void init()       // 初始化所有的物体
 {
  for (int a = 0; a < numOfMasses; ++a)    
   masses[a]->init();     
 }

 virtual void solve()       //虚函数,在具体的应用中设置各个施加给各个物体的力
 {
         
 }

 virtual void simulate(float dt)      //让所有的物体模拟一步
 {
  for (int a = 0; a < numOfMasses; ++a)    
   masses[a]->simulate(dt);    
 }

 ...

整个模拟的部分被封装到下面的函数中 
  
 (class Simulation continued)

 virtual void operate(float dt)      //  完整的模拟过程
 {
  init();        // 设置力为0
  solve();        // 应用力
  simulate(dt);       // 模拟
 }
};

现在我们已经有了一个简单的物理模拟引擎了,它包含有物体和模拟两个类,下面我们基于它们创建三个具体的模拟对象:

1. 具有恒定速度的物体
2. 具有恒定加速度的物体
3. 具有与距离成反比的力的物体

在程序中控制一个模拟对象:

在我们写一个具体的模拟类之前,让我们看看如何在程序中模拟一个对象,在这个教程里,模拟引擎和操作模拟的程序在两个文件里,在程序中我们使用如下的函数,操作模拟:

void Update (DWORD milliseconds)      // 执行模拟

这个函数在每一帧的开始更新,参数为相隔的时间。 
  
void Update (DWORD milliseconds)
{
 ...
 ...
 ...

 float dt = milliseconds / 1000.0f;     // 转化为秒

 dt /= slowMotionRatio;      // 除以模拟系数

 timeElapsed += dt;       // 更新流失的时间

 ...

在下面的代码中,我们定义一个处理间隔,没隔这么长时间,让物理引擎模拟一次。 

 ...

 float maxPossible_dt = 0.1f;     // 设置模拟间隔
         

   int numOfIterations = (int)(dt / maxPossible_dt) + 1;   //计算在流失的时间里模拟的次数
 if (numOfIterations != 0)     
  dt = dt / numOfIterations;     

 for (int a = 0; a < numOfIterations; ++a)    // 模拟它们 
 {
  constantVelocity->operate(dt);     
  motionUnderGravitation->operate(dt);    
  massConnectedWithSpring->operate(dt);    
 }
}

下面让我们来写着两个具体的模拟类:

1. 具有恒定速度的物体
* class ConstantVelocity : public Simulation ---> 模拟一个匀速运动的物体

class ConstantVelocity : public Simulation
{
public:
 ConstantVelocity() : Simulation(1, 1.0f)    
 {
  masses[0]->pos = Vector3D(0.0f, 0.0f, 0.0f);   // 初始位置为0
  masses[0]->vel = Vector3D(1.0f, 0.0f, 0.0f);   // 向右运动
 }
};

下面我们来创建一个具有恒定加速的物体:

class MotionUnderGravitation : public Simulation
{
 Vector3D gravitation;       // 加速度

 MotionUnderGravitation(Vector3D gravitation) : Simulation(1, 1.0f)  //  构造函数
 {         
  this->gravitation = gravitation;     // 设置加速度
  masses[0]->pos = Vector3D(-10.0f, 0.0f, 0.0f);   // 设置位置为左边-10处
  masses[0]->vel = Vector3D(10.0f, 15.0f, 0.0f);   // 设置速度为右上
 }

 ...

下面的函数设置施加给物体的力    

 virtual void solve()       // 设置当前的力
 {
  for (int a = 0; a < numOfMasses; ++a)    
   masses[a]->applyForce(gravitation * masses[a]->m); 
 }

下面的类创建一个受到与距离成正比的力的物体: 
  
class MassConnectedWithSpring : public Simulation
{
public:
 float springConstant;       // 弹性系数
 Vector3D connectionPos;       // 连接方向

 MassConnectedWithSpring(float springConstant) : Simulation(1, 1.0f)  // 构造函数
 {
  this->springConstant = springConstant;    

  connectionPos = Vector3D(0.0f, -5.0f, 0.0f);  

  masses[0]->pos = connectionPos + Vector3D(10.0f, 0.0f, 0.0f); 
  masses[0]->vel = Vector3D(0.0f, 0.0f, 0.0f);  
 }

 ...

下面的函数设置当前物体所受到的力: 
  
virtual void solve()        // 设置当前的力
{
 for (int a = 0; a < numOfMasses; ++a)     
 {
  Vector3D springVector = masses[a]->pos - connectionPos;  
  masses[a]->applyForce(-springVector * springConstant);  
 }
}
原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=39

相关文章:

max(min)-device-width和max(min)-width的区别

max-device-width和max-width的区别表现在如下几个方面&#xff1a; 1. max-device-width是设备整个显示区域的宽度&#xff0c;例如&#xff0c;真实的设备屏幕宽度。 2. max-width是目标显示区域的宽度&#xff0c;例如&#xff0c;浏览器宽度。 3. 如果使用max-device-width…

一篇文看懂Hadoop

我们很荣幸能够见证Hadoop十年从无到有&#xff0c;再到称王。感动于技术的日新月异时&#xff0c;希望通过这篇内容深入解读Hadoop的昨天、今天和明天&#xff0c;憧憬下一个十年。 本文分为技术篇、产业篇、应用篇、展望篇四部分 技术篇 2006年项目成立的一开始&#xff0c;“…

MaxCompute动态更新表中某个(多个)字段的数据

功能 MaxCompute支持了delete、update功能&#xff0c;但当您需要使用多个insert、update、delete对目标表进行批量操作时&#xff0c;需要编写多条SQL语句&#xff0c;然后进行多次全表扫描才能完成操作。MaxCompute提供的merge into功能&#xff0c;只需要进行一次全表扫描操…

字符串数组的赋值

例如:main(){chars[30]; strcpy(s,"Good News!"); /*给数组赋字符串*/ }上面程序在编译时,遇到chars[30]这条语句时,编译程序会在内存的某处留出连续30个字节的区域, 并将第一个字节的地址赋给s。当遇到strcpy( strcpy 为TurboC2.0的函数)时, 首先在目标文件的某处建…

NetBeans使用介绍(五)

第9章 Swing菜单 Swing菜单是我们经常用到的一种控件&#xff0c;NetBeans对菜单进行了很好的封装&#xff0c;是我们应用起来非常方便。下面&#xff0c;我们就来简单的了解一下Swing菜单。 菜单&#xff1a;Jmenu 菜单项&#xff1a;JmenuItem 复选菜单项&#xff1a;JcheckB…

Pycharm中如何安装python库

1首先打开pycharm工具&#xff0c;选择File中的Setting选项&#xff0c;如下图所示 2在打开的setting界面中我们点击python的解释器&#xff0c;你会看到很多导入的第三方库&#xff0c;如下图所示&#xff0c;点击最右边的加号 3在弹出的available packages界面中&#xff0c;…

LINUX下SVN命令大全

1、将文件checkout到本地目录 svn checkout path&#xff08;path是服务器上的目录&#xff09;例如&#xff1a;svn checkout svn://192.168.1.1/pro/domain简写&#xff1a;svn co2、往版本库中添加新的文件 svn addfile例如&#xff1a;svn addtest.php(添加test.php)svn ad…

MaxCompute 多行数据合并为一行数据

SELECT class, wm_concat(distinct ,, name) FROM students GROUP BY class; 参考

python学习之循环语句的九九乘法表

while 语句的九九乘法表&#xff1a; ##九九乘法表#总共有九行# 每行中的列数&#xff0c;就是当前所处的行号#乘式的第一个数代表的是列&#xff0c;第二个数代表的是行row 1#行column 1#列while row < 9: while column < row: print(%d * %d %d, %(column…

影著协公布的使用费收取标准

网络&#xff1a;距首映两年内的电影基本使用费下限为10万元/部年。 网吧&#xff1a;每天的使用费为&#xff1a;电脑总量网吧每小时收费标准7.5%。 视频点播:基本使用费为每次点击的单价33.3%终端用户数10%。 飞机和火车&#xff1a;每年每部影片使用费为2500元~5万元。 轮船…

POJ.3207.Ikki's Story IV-Panda's Trick(2-SAT)

题目链接 \(Description\) 一个圆上顺序排列0,1,...,n-1共n个点&#xff0c;给出m条线段&#xff0c;线段可以从里面连也可以从外面连&#xff0c;问是否能满足所有线段不相交 \(Solution\) 把每条线段看做一个点&#xff0c;只有在圆外和在圆内两种情况&#xff0c;于是可以把…

sqlconnection,sqlcommand,SqlDataAdapter ,ExecuteNonQuery,ExecuteScalar

sqlconnection&#xff1a;表示 SQL Server 数据库的一个打开的连接。SqlConnection 对象表示与 SQL Server 数据源的一个唯一的会话。 在客户端/服务器数据库系统中&#xff0c;它等效于一个到服务器的网络连接。 SqlConnection 与 SqlDataAdapter 和 SqlCommand 一起使用&…

swift - idfa(唯一标示/下载量/广告追踪)

//设置-隐私-广告-限制广告跟踪关闭if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {DDLOG(message: "IDFA:\(ASIdentifierManager.shared().advertisingIdentifier.uuidString)")}转载于:https://www.cnblogs.com/qingzZ/p/11046424.html

正则去除括号内容

var str"123{xxxx}456[我的]789123[你的]456(1389090)(\n1389090)789";var regex1 /\((.?)\)/g; // () 小括号var regex4 /\((\n.?)\)/g; // (\n) 小括号var regex2 /\[(.?)\]/g; // [] 中括号var regex3 /\{(.?)\}/g; // {} 花括号&#xff0c;大括号// 输出…

通过shell将*.txt文本内容批量录入mysql

想将某*.txt文本内容全部录入到mysql的某个数据表中&#xff0c;通过使用load data local infile into table table_name ....但感觉用的不够溜、导致经常格式有问题&#xff0c;很难达到我要的效果。。 所以&#xff0c;自己想了下&#xff0c;最后通过shell成功将批量数据导入…

String、StringBuilder、StringBuffer的比较

最近学习中又遇到了StringBuilder&#xff0c;突然就联想到StringBuffeer和String。于是翻翻笔记还在。那么他们有什么区别&#xff1f; 1.StringBuilder与StringBuffer StringBuilder&#xff1a;线程非安全&#xff0c;一般在单线程中操作字符串缓冲区使用&#xff0c;速度最…

Autojs自动化 实现自动删除公众号文章(通过订阅号助手删除)

设备 小米9 效果 autojs1autojs2代码 UI "ui"; const storage storages.create("dinyue"); auto()var search_value if(storage.contains(search_value)){search_value storage.get(search_value) }ui.layout(<vertical padding"30">…

[转]Java中常用的集合—初学者的你不可错过的精编整理

集合一直都是项目中非常常见的&#xff0c;我是一个Android开发者&#xff0c;集合对于我来说&#xff0c;在项目中使用的次数非常之多&#xff0c;因为使用的多&#xff0c;熟能生巧&#xff0c;所以这里呢&#xff01;就给那些初学者整理一下Java当中常用的集合吧&#xff01…

通过tinyxml解析百度热词榜单

百度热词榜单 初学TinyXml&#xff0c;盯上了解析百度热词榜单。通过curl获取rss地址上的数据。 由于TinyXml不支持&#xff0c;所以要转换为UTF-8格式&#xff0c;命令为 iconv -f gb2312 -t utf-8 baidutop.xml > baidutop.xml 写得很烂&#xff0c;写的时候&#xff0c;心…

台湾MCI报告:Security SaaS风潮渐起

这篇文章是台湾产业情报研究所&#xff08;MCI&#xff09;在2008年所作的一份有关Security-as-a-Serivce&#xff08;Security SaaS&#xff0c;为了区别Software-as-a-Serivce&#xff0c;特称呼为Security SaaS&#xff09;的报告。这里有该文章的繁体版下载。这份报告引用了…

由“公共类”看出的一些东西

如果一个类想要被其他类引用&#xff0c;即使是在同一个命名空间下&#xff0c;也要是public&#xff0c;才能被引用。同样&#xff0c;如果想让别人交流&#xff0c;首先自己应该先要“开放”。转载于:https://www.cnblogs.com/HuShaoyi/p/8503708.html

H-ui 前端框架

H-ui 前端框架 架起设计与后端的桥梁轻量级前端框架&#xff0c;简单免费&#xff0c;兼容性好&#xff0c;服务中国网站。 首个付费版产品 H-ui.admin.Pro&#xff0c;盘他&#xff01;转载于:https://www.cnblogs.com/Jeely/p/11051079.html

使用.NET FileSystemWatcher对象监控磁盘文件目录的改变

原文&#xff08;英文&#xff09;&#xff1a;http://www.techrepublic.com/article/use-the-net-filesystemwatcher-object-to-monitor-directory-changes-in-c/6165137 许多情况下&#xff0c;如果创建或修改一个文件&#xff0c;系统需要执行某些任务。例如&#xff0c;在一…

前端使用crypto.js进行加密学习记录

因为我的需求是加密可逆&#xff0c;具有一定的安全性(对安全性要求不高)&#xff0c;所以使用DES或AES即可&#xff0c;我用的是AES js下载 下载 Nodejs后端&#xff1a;对用户密码进行加密 - WJY- - 博客园Nodejs后端 对用户密码进行加密 使用 bcryptjs 插件对用户密码进行…

Exchange笔记之使用OWA加密访问邮箱

Exchange笔记之使用OWA加密访问邮箱 OWA是什么&#xff1f;呵呵&#xff0c;应该用惯Exchange的用户来说都知道&#xff0c;我在这里简单说下&#xff1a;OWA是一种给予Exchange客户端的一种B/S结构访问方式。OWA是Outlook Web Access的缩写&#xff0c;OWA在我们平时使用的时候…

LInkedHashMap实现最近被使用(LRU)缓存

在最近的面试中&#xff0c;我曾被多次问到&#xff0c;怎么实现一个最近最少使用&#xff08;LRU&#xff09;的缓存。缓存可以通过哈希表来实现&#xff0c;然而为这个缓存增加大小限制会变成另一个有意思的问题。现在我们看一下怎么实现。 最近最少使用缓存的回收 为了实现缓…

2019年9月2日开学!寒假时间也定了……

原文&#xff1a; https://mp.weixin.qq.com/s/Dns-ucDwuDeR7lNSlibyAA 放假通知 今年7月1日放暑假 9月2日开学 今天&#xff0c;省教育厅发布通知&#xff0c;2019年全省中小学幼儿园暑期放假时间统一为7月1日&#xff0c;秋季开学时间9月2日。2020年寒假放假时间为1月18日&am…

Vue 增加动态路由功能 【在原有系统上增加】

目录 遇到问题 1. 修改router/index.js 2. 修改 store文件夹下的 2.1 增加 modules/permission.js 2.2 增加modules/tagsViews.js 2.3 修改modules/user.js 2.4 修改getter.js 2.5 修改index.js 遇到问题 1.当出现循环刷新页面&#xff0c;不断进行请求时&#xff0c;检查配…

读取 android的内存、cpu、流量等 信息

内存总量&#xff1a;/proc/meminfocpu信息&#xff1a;/proc/cpuinfocpu使用率&#xff1a;/proc/stat流量信息&#xff1a;/proc/self/net/dev /proc/net/devetc/network/interfaces 这个文件是保存ip,netmask,gateway信息的&#xff08;包括静态和动态&#xff09;&#xff…

CCNA基础知识汇总

本资源是本人学习的过程中的一些笔记和学习中用到的文档。主要包括ospf&#xff0c;ppp&#xff0c;ACL与策略路由&#xff0c;帧中继&#xff0c;***方面。希望能对大家CCNA的学习有所帮助。下载地址&#xff1a;http://down.51cto.com/data/128047 转载于:https://blog.51cto…