转自:

How Physics Engines Work

高中物理全还给老师了啊啊啊啊啊啊


牛顿第二定律

物体加速度的大小跟物体受到的作用力成正比,跟物体的质量成反比,加速度的方向跟合外力的方向相同。 而以物理学的观点来看,牛顿运动第二定律亦可以表述为“物体随时间变化之动量变化率和所受外力之和成正比”,即动量对时间的一阶导数等 于外力之和。牛顿第二定律说明了在宏观低速下,a∝F/m,F∝ma,用数学表达式可以写成F=kma,其中的k为比例系数,是一个常数。但由于当时没有 规定多大的力作为力的单位,比例系数k的选取就有一定的任意性,如果取k=1,就有F=ma,这就是今天我们熟知的牛顿第二定律的数学表达式。

The process for simulating an object’s motion goes something like this:

  1. Figure out what the forces are on an object
  2. Add those forces up to get a single “resultant” or “net” force  更新F
  3. Use F = ma to calculate the object’s acceleration due to those forces  计算加速度a
  4. Use the object’s acceleration to calculate the object’s velocity  计算速度v
  5. Use the object’s velocity to calculate the object’s position  计算位置
  6. Since the forces on the object may change from moment to moment, repeat this process from #1, forever 迭代
last_acceleration = acceleration
position += velocity * time_step + ( 0.5 * last_acceleration * time_step^2 )
new_acceleration = force / mass
avg_acceleration = ( last_acceleration + new_acceleration ) / 2
velocity += avg_acceleration * time_step

通常涉及到的简单的力:

  • weight force   重力
  • spring force   弹簧
  • viscous damping force  阻尼
  • air drag 空气阻力  $F_D=\frac{1}{2}\rho v^2C_DA$

示例:一维, 重力+空气阻力+阻尼

/*
The following is not free software. You may use it for educational purposes, but you may not redistribute or use it commercially.
(C) Burak Kanber 2012
*/var canvas = document.getElementById('canvas'),ctx = canvas.getContext('2d'),height = 400,width = 400,x = 200,y = 0,vy = 0,ay = 0,m = 0.1,    // Ball mass in kgr = 10,     // Ball radius in cm, or pixels.dt = 0.02,  // Time step.e = -0.5,   // Coefficient of restitution ("bounciness")rho = 1.2,  // Density of air. Try 1000 for water.C_d = 0.47, // Coeffecient of drag for a ballA = Math.PI * r * r / 10000 // Frontal area of the ball; divided by 10000 to compensate for the 1px = 1cm relation
    ;ctx.fillStyle = 'red';function loop()
{ var fy = 0;/* Weight force, which only affects the y-direction (because that's the direction gravity points). */fy += m * 9.81;  //重力/* Air resistance force; this would affect both x- and y-directions, but we're only looking at the y-axis in this example. */fy += -1 * 0.5 * rho * C_d * A * vy * vy; //空气阻力/* Verlet integration for the y-direction */dy = vy * dt + (0.5 * ay * dt * dt);  //计算位置/* The following line is because the math assumes meters but we're assuming 1 cm per pixel, so we need to scale the results */y += dy * 100;new_ay = fy / m;avg_ay = 0.5 * (new_ay + ay);vy += avg_ay * dt;/* Let's do very simple collision detection */if (y + r > height && vy > 0){/* This is a simplification of impulse-momentum collision response. e should be a negative number, which will change the velocity's direction. */vy *= e; //碰撞,反向,速度减小/* Move the ball back a little bit so it's not still "stuck" in the wall. */y = height - r;  //避免重复判断                      }draw();
}function draw()
{ctx.clearRect(0, 0, width, height);ctx.beginPath();ctx.arc(x, y, r, 0, Math.PI * 2, true);ctx.fill();ctx.closePath();
}/* A real project should use requestAnimationFrame, and you should time the frame rate and pass a variable "dt" to your physics function. This is just a simple brute force method of getting it done. */
setInterval(loop, dt * 1000);

对于全三维的情况,需要6个维度来描述(x,y,z维度的位移和旋转):

translation in x, y, and z (called “sway”, “heave” and “surge”), and rotation about x, y, and z (called “pitch”, “yaw”, and “roll”).


旋转+弹簧

position, velocity, and acceleration change  : 位置、速度、加速度
rotation angle, angular velocity, and angular acceleration : 角度、角速度、角加速度

看不下去了啊

/*
The following is not free software. You may use it for educational purposes, but you may not redistribute or use it commercially.
(C) Burak Kanber 2012
*/
var canvas = document.getElementById('canvas'),ctx = canvas.getContext('2d'),height = 400,width = 400,stiffness = 0.5,b = -1,angularB = -7,dt = 0.02;/* A more generic vector class could take an arbitrary number of elements, rather than just x, y, or z. This vector class is strictly 2D */
var V = function(x, y) {this.x = x;this.y = y;
};V.prototype.add = function(v) {return new V(v.x + this.x, v.y + this.y);
};V.prototype.subtract = function(v) {return new V(this.x - v.x, this.y - v.y);
};V.prototype.scale = function(s) {return new V(this.x * s, this.y * s);
};V.prototype.dot = function(v) {return (this.x * v.x + this.y * v.y);
};/* Normally the vector cross product function returns a vector. But since we know that all our vectors will only be 2D (x and y only), any cross product we calculate will only have a z-component. Since we don't have a 3D vector class, let's just return the z-component as a scalar. We know that the x and y components will be zero. This is absolutely not the case for 3D. */
V.prototype.cross = function(v) {return (this.x * v.y - this.y * v.x);
};V.prototype.rotate = function(angle, vector) {var x = this.x - vector.x;var y = this.y - vector.y;var x_prime = vector.x + ((x * Math.cos(angle)) - (y * Math.sin(angle)));var y_prime = vector.y + ((x * Math.sin(angle)) + (y * Math.cos(angle)));return new V(x_prime, y_prime);
};/* Our simple rectangle class is defined by the coordinates of the top-left corner, the width, height, and mass. */
var Rect = function(x, y, w, h, m) {if (typeof(m) === 'undefined') {this.m = 1;}this.width = w;this.height = h;this.topLeft = new V(x, y);this.topRight = new V(x + w, y);this.bottomRight = new V(x + w, y + h);this.bottomLeft = new V(x, y + h);this.v = new V(0, 0);this.a = new V(0, 0);this.theta = 0;this.omega = 0;this.alpha = 0;this.J = this.m * (this.height*this.height + this.width*this.width) / 12000;
};/* The Rect class only defines the four corners of the rectangle, but sometimes we need the center point so we can calulate that by finding the diagonal and cutting it in half. */
Rect.prototype.center = function() {var diagonal = this.bottomRight.subtract(this.topLeft);var midpoint = this.topLeft.add(diagonal.scale(0.5));return midpoint;
};/* To rotate a rectangle we'll update both its angle and rotate all four of the corners */
Rect.prototype.rotate = function(angle) {this.theta += angle;var center = this.center();this.topLeft = this.topLeft.rotate(angle, center);this.topRight = this.topRight.rotate(angle, center);this.bottomRight = this.bottomRight.rotate(angle, center);this.bottomLeft = this.bottomLeft.rotate(angle, center);return this;
};/* Simply translate all four corners */
Rect.prototype.move = function(v) {this.topLeft = this.topLeft.add(v);this.topRight = this.topRight.add(v);this.bottomRight = this.bottomRight.add(v);this.bottomLeft = this.bottomLeft.add(v);return this;
}var rect = new Rect(200, 0, 100, 50);
rect.v = new V(0, 2);
var spring = new V(200, 0);ctx.strokeStyle = 'black';var loop = function() {var f = new V(0, 0);var torque = 0;/* Start Velocity Verlet by performing the translation */var dr = rect.v.scale(dt).add(rect.a.scale(0.5 * dt * dt));rect.move(dr.scale(100));/* Add Gravity */f = f.add(new V(0, rect.m * 9.81));/* Add damping */f = f.add( rect.v.scale(b) );/* Add Spring; we calculate this separately so we can calculate a torque. */var springForce = rect.topLeft.subtract(spring).scale(-1 * stiffness);/* This vector is the distance from the end of the spring to the box's center point */var r = rect.center().subtract(rect.topLeft);/* The cross product informs us of the box's tendency to rotate. */var rxf = r.cross(springForce);torque += -1 * rxf;f = f.add(springForce);/* Finish Velocity Verlet */var new_a = f.scale(rect.m);var dv = rect.a.add(new_a).scale(0.5 * dt);rect.v = rect.v.add(dv);/* Do rotation; let's just use Euler for contrast */torque += rect.omega * angularB; // Angular dampingrect.alpha = torque / rect.J;rect.omega += rect.alpha * dt;var deltaTheta = rect.omega * dt;rect.rotate(deltaTheta);draw();
};var draw = function() {ctx.strokeStyle = 'black';ctx.clearRect(0, 0, width, height);ctx.save();ctx.translate(rect.topLeft.x, rect.topLeft.y);ctx.rotate(rect.theta);ctx.strokeRect(0, 0, rect.width, rect.height);ctx.restore();ctx.strokeStyle = '#cccccc';ctx.beginPath();ctx.moveTo(spring.x,spring.y);ctx.lineTo(rect.topLeft.x, rect.topLeft.y);ctx.stroke();ctx.closePath();
};setInterval(loop, dt*1000);


碰撞检测

/*
The following is not free software. You may use it for educational purposes, but you may not redistribute or use it commercially.
(C) Burak Kanber 2012
*/
var canvas,ctx,height = 400,width = 400,stiffness = 0.5,b = -1,angularB = -1,dt = 0.02;
Array.prototype.max = function() {return Math.max.apply(null, this);
};Array.prototype.min = function() {return Math.min.apply(null, this);
};
var V = function(x, y) {this.x = x;this.y = y;
};V.prototype.length = function() {return Math.sqrt(this.x*this.x + this.y*this.y);
};V.prototype.add = function(v) {return new V(v.x + this.x, v.y + this.y);
};V.prototype.subtract = function(v) {return new V(this.x - v.x, this.y - v.y);
};V.prototype.scale = function(s) {return new V(this.x * s, this.y * s);
};V.prototype.dot = function(v) {return (this.x * v.x + this.y * v.y);
};V.prototype.cross = function(v) {return (this.x * v.y - this.y * v.x);
};V.prototype.toString = function() {return '[' + this.x + ',' + this.y + ']';
};V.prototype.rotate = function(angle, vector) {var x = this.x - vector.x;var y = this.y - vector.y;var x_prime = vector.x + ((x * Math.cos(angle)) - (y * Math.sin(angle)));var y_prime = vector.y + ((x * Math.sin(angle)) + (y * Math.cos(angle)));return new V(x_prime, y_prime);
};
var Rect = function(x, y, w, h, m) {if (typeof(m) === 'undefined') {this.m = 1;}this.width = w;this.height = h;this.active = true;this.topLeft = new V(x, y);this.topRight = new V(x + w, y);this.bottomRight = new V(x + w, y + h);this.bottomLeft = new V(x, y + h);this.v = new V(0, 0);this.a = new V(0, 0);this.theta = 0;this.omega = 0;this.alpha = 0;this.J = this.m * (this.height * this.height + this.width * this.width) / 12000;
};Rect.prototype.center = function() {var diagonal = this.bottomRight.subtract(this.topLeft);var midpoint = this.topLeft.add(diagonal.scale(0.5));return midpoint;
};Rect.prototype.rotate = function(angle) {this.theta += angle;var center = this.center();this.topLeft = this.topLeft.rotate(angle, center);this.topRight = this.topRight.rotate(angle, center);this.bottomRight = this.bottomRight.rotate(angle, center);this.bottomLeft = this.bottomLeft.rotate(angle, center);return this;
};Rect.prototype.move = function(v) {this.topLeft = this.topLeft.add(v);this.topRight = this.topRight.add(v);this.bottomRight = this.bottomRight.add(v);this.bottomLeft = this.bottomLeft.add(v);return this;
};Rect.prototype.draw = function(ctx) {ctx.strokeStyle = 'black';ctx.save();ctx.translate(this.topLeft.x, this.topLeft.y);ctx.rotate(this.theta);ctx.strokeRect(0, 0, this.width, this.height);ctx.restore();
};
Rect.prototype.vertex = function(id)
{if (id == 0){return this.topLeft;}else if (id == 1){return this.topRight;}else if (id == 2){return this.bottomRight;}else if (id == 3){return this.bottomLeft;}
};
function intersect_safe(a, b)
{var result = new Array();var as = a.map( function(x) { return x.toString(); });var bs = b.map( function(x) { return x.toString(); });for (var i in as){if (bs.indexOf(as[i]) !== -1){result.push( a[i] );}}return result;
}satTest = function(a, b) {var testVectors = [a.topRight.subtract(a.topLeft),a.bottomRight.subtract(a.topRight),b.topRight.subtract(b.topLeft),b.bottomRight.subtract(b.topRight),];var ainvolvedVertices = [];var binvolvedVertices = [];/** Look at each test vector (shadows)*/for (var i = 0; i < 4; i++) {ainvolvedVertices[i] = []; // Our container for involved vertcesbinvolvedVertices[i] = []; // Our container for involved vertcesvar myProjections = [];var foreignProjections = [];for (var j = 0; j < 4; j++) {myProjections.push(testVectors[i].dot(a.vertex(j)));foreignProjections.push(testVectors[i].dot(b.vertex(j)));}// Loop through foreignProjections, and test if each point is x lt my.min AND x gt m.max// If it's in the range, add this vertex to a listfor (var j in foreignProjections) {if (foreignProjections[j] > myProjections.min() && foreignProjections[j] < myProjections.max()) {binvolvedVertices[i].push(b.vertex(j));}}// Loop through myProjections and test if each point is x gt foreign.min and x lt foreign.max// If it's in the range, add the vertex to the listfor (var j in myProjections) {if (myProjections[j] > foreignProjections.min() && myProjections[j] < foreignProjections.max()) {ainvolvedVertices[i].push(a.vertex(j));}}}// console.log( intersect_safe ( intersect_safe( involvedVertices[0], involvedVertices[1] ), intersect_safe( involvedVertices[2], involvedVertices[3] ) ) );ainvolvedVertices = intersect_safe(intersect_safe(ainvolvedVertices[0], ainvolvedVertices[1]), intersect_safe(ainvolvedVertices[2], ainvolvedVertices[3]));binvolvedVertices = intersect_safe(intersect_safe(binvolvedVertices[0], binvolvedVertices[1]), intersect_safe(binvolvedVertices[2], binvolvedVertices[3]));
/*If we have two vertices from one rect and one vertex from the other, probably the single vertex is penetrating the segmentreturn involvedVertices;*/if (ainvolvedVertices.length === 1 && binvolvedVertices.length === 2){return ainvolvedVertices[0];}else if (binvolvedVertices.length === 1 && ainvolvedVertices.length === 2){return binvolvedVertices[0];}else if (ainvolvedVertices.length === 1 && binvolvedVertices.length === 1){return ainvolvedVertices[0];}else if (ainvolvedVertices.length === 1 && binvolvedVertices.length === 0){return ainvolvedVertices[0];}else if (ainvolvedVertices.length === 0 && binvolvedVertices.length === 1){return binvolvedVertices[0];}else if (ainvolvedVertices.length === 0 && binvolvedVertices.length === 0){return false;}else{console.log("Unknown collision profile");console.log(ainvolvedVertices);console.log(binvolvedVertices);clearInterval(timer);}return true;}var rect = new Rect(200, 0, 100, 50);
var wall = new Rect(125, 200, 100, 50);
rect.omega = -10;var loop = function() {var f = new V(0, 0);var torque = 0;/* Start Velocity Verlet by performing the translation */var dr = rect.v.scale(dt).add(rect.a.scale(0.5 * dt * dt));rect.move(dr.scale(100));/* Add Gravity */f = f.add(new V(0, rect.m * 9.81));/* Add damping */f = f.add(rect.v.scale(b));/* Handle collision */var collision = satTest(rect, wall);if (collision){var N = rect.center().subtract(collision); //.rotate(Math.PI , new V(0,0));N = N.scale( 1 / N.length());var Vr = rect.v;var I =  N.scale( -1 * (1 + 0.3) * Vr.dot(N) );rect.v = Irect.omega = -1 * 0.2 * (rect.omega / Math.abs(rect.omega)) * rect.center().subtract(collision).cross(Vr);}/* Finish Velocity Verlet */var new_a = f.scale(rect.m);var dv = rect.a.add(new_a).scale(0.5 * dt);rect.v = rect.v.add(dv);/* Do rotation; let's just use Euler for contrast */torque += rect.omega * angularB; // Angular dampingrect.alpha = torque / rect.J;rect.omega += rect.alpha * dt;var deltaTheta = rect.omega * dt;rect.rotate(deltaTheta);draw();
};var draw = function() {ctx.clearRect(0, 0, width, height);rect.draw(ctx);wall.draw(ctx);};var timer;canvas = document.getElementById('canvas'),ctx = canvas.getContext('2d'),ctx.strokeStyle = 'black';timer = setInterval(loop, dt * 1000);

转载于:https://www.cnblogs.com/guoqiaojin/p/3182435.html

[翻译] 物理引擎javascript实现相关推荐

  1. Verlet-js JavaScript 物理引擎

    subprotocol最近在Github上开源了verlet-js.地址为https://github.com/subprotocol/verlet-js.verlet-js是一个集成Verlet的物 ...

  2. JavaScript 的物理引擎对比

    在本文中,我们将对比看一下当前三个非常流行的和一个目前还在开发中的JavaScript 物理引擎库,分别是: box2dweb,Ammo.js,JigLibJS 以及 Connon.js.我们会简短的 ...

  3. havok物理引擎快速入门指南翻译

    花了2天时间完成了这个翻译, 需要有C++基础, 是通过haovk引擎做物理动画的基础. 到intel官方网站注册一个帐号可以免费获取havok SDK, 这是SDK中的一个文档的翻译. [翻译]Ha ...

  4. 机器学习 物理引擎_在太阳物理学中使用机器学习

    机器学习 物理引擎 我们如何预测冠状物质抛射? (How Can We Predict Coronal Mass Ejections?) Coronal Mass Ejection (CME) is ...

  5. 《Unity 4 3D开发实战详解》一6.7 物理引擎综合案例

    本节书摘来异步社区<Unity 4 3D开发实战详解>一书中的第6章,第6.7节,作者: 吴亚峰 , 杜化美 , 张月霞 , 索依娜 责编: 张涛,更多章节内容可以访问云栖社区" ...

  6. 游戏引擎开发和物理引擎_视频游戏开发的最佳游戏引擎

    游戏引擎开发和物理引擎 In this article, we'll look at some of the most popular game engines for video game deve ...

  7. Egret中使用P2物理引擎

    游戏中的对象按照物理规律移动,体现重力.引力.反作用力.加速度等物体特性,实现自由落体.摇摆运动.抛物线运动,以及物理碰撞现象的模拟.用于模拟物理碰撞.物理运动的引擎称为物理引擎.  来自瑞典斯德哥尔 ...

  8. Box2D物理引擎入门

    一.什么是Box2D Box2D是一个强大的开源物理游戏引擎,用来模拟2D刚体物体运动和碰撞,由Erin Catto早在2007年用C++语言开发. Box2D集成了大量的物理力学和运动学的计算,并将 ...

  9. 【日常点滴016】python游戏库arcade结合Tiled map editor地图编辑器和Pymunk物理引擎制作游戏过程记录,并最终打包为exe文件

    独此一家,建议收藏 前言 一.创建一个空白窗口 step001.py代码示例 二.创建很多全局能用的常量 step002.py代码示例 三.创建实例变量即代表各种精灵等的变量 step003.py代码 ...

最新文章

  1. linux中文输入法的实现
  2. 基于Maven管理的Mapreduce程序下载依赖包到LIB目录
  3. mysql数据类型优化
  4. < meta name=“viewport“ content=“width=device-width, initial-scale=1.0“>的解释
  5. android卡片层叠效果_ReactNative之Android绝对布局position:#x27;absolute#x27;问题
  6. word文本框跨页显示方法
  7. Oracle序列号详解
  8. Python写数据结构:循环队列
  9. DDZY719-Z型单相费控智能电能表通过Rola模块实现的无线抄表实验
  10. 常用的富文本编辑器插件
  11. 高防服务器的机房硬件防火墙类型和主要功能
  12. 浅析凸优化理论学习中的“坑”
  13. 十年中国WLAN市场霸主,是谁?
  14. 密信Mesign本地部署企业密钥管理系统解决方案
  15. 利用python通过字幕文件.srt来实现对视频片段的截取
  16. windows 资源管理器已停止工作的解决办法
  17. php+laravel 扫码二维码签到
  18. MacBook无故黑屏,按键没反应解决办法
  19. 实现点击选择按钮时候选中对应选中的行,当点击某一行单元格时候就清除其他选中行然后选中对应点击的那一行
  20. Vivado与modelsim联合仿真(2018.3---10.6c)

热门文章

  1. 20200503:力扣187周周赛上
  2. 高级工计算机操作试题及答案,计算机系统操作高级工试题和答案[1]
  3. 500错误无法为jsp编译类:_JSP(Java Server Pages)的相关使用
  4. VB禁用CTRL +ALT + DEL(1)
  5. 灰度实战(一):Apollo配置中心(1)
  6. 马斯克圣诞节晒太空飞船:明年4月测试,5年后飞火星
  7. CNN推理哪家强?英伟达/英特尔/骁龙/麒麟/ActionSemi大测评
  8. 一头盔一电极,MIT机器人读心完毕
  9. 对是否要用Linux的思考
  10. LSTM神经网络输入输出究竟是怎样的?