|

用代码描述自然系统-万有引力与光速上限

上篇博文介绍了使用向量模拟物体受力,并使用向量完成物体的受力分析,本篇继续模拟自然系统。在完成对力的模拟和定义以后,本篇博文将实现万有引力,并使用万有引力实现自由落体。

万有引力

根据牛顿力学定律,万有引力是任何宏观物体的基本属性,计算万有引力可以使用下面的公式:

F = \frac{m_1 * m_2}{r^2} * G

假设地球质量为 M ,物体质量为 m ,引力常数为 G ,那么对于任意一个地球上的物体,其受到的地球的万有引力可以表示为:

F = \frac{m * M}{r^2} * G

在地球上,任何一个物体受到地球的引力都可以转变为加速度,也就是重力加速度,这个加速度与物体质量无关,用上面的公式表示为:

g = \frac{F}{m} = \frac{m * M}{r^2 * m} * G = \frac{M * G}{r^2}

地球质量与万有引力常数可以认为是常量,那么物体受到的重力加速度将只与物体与地球质心之间的距离有关,这个距离一般可以认为是物体距离地面的高度。在近地面附近,高度带来的重力加速度变化很小,一般取 9.8

自由落体

基于上面的物理公式,重新描述 Mover 的受力情况,首先定义基本常数如下:

// 万有引力常数
const G = 6.67259E-11;
// 地球质量
const M = 5.965E24;
// 地球半径
const D = 6371000;

接下来重写 Mover 的受力部分,一个物体的受力,应该转变为加速度,然后将加速度累加在速度上。

class Mover {
  // ...
  addForce(force) {
    const a = force.div(this.mass); // 加速度 a = F / m
    this.velocity.add(a);
  }
  // ...
}

然后在每一个计算周期中重新计算物体受到地球的万有引力,将万有引力施加到物体上。

function draw() {
  background(200);
  movers.forEach(mover => {
    // 定义一个垂直向下的单位向量
    const directionVector = createVector(0, 1).normalize();
    // 计算万有引力的值
    const f = mover.mass * M / Math.pow(PHeight - mover.position.y + D, 2) * G;
    // 将万有引力转为矢量施加到物体上
    mover.addForce(directionVector.mult(f));
    mover.update();
    mover.display();
  });
}

基于上面的实现,原来的效果会变成下面这样:

上面的示例效果中可以看到物体下落的太快,不是很友好,下面来介绍如何改进。

上帝之手

要实现对上述示例的改进,让其看起来更自然,可以通过修改万有引力常数 G 来实现,在这个二维物理世界中,你将是上帝一般的存在,简简单单修改一个常数,就能让世界发生巨大变化,后续的博文中将深入介绍。

我们将万有引力常数 G 减小 100 倍,其他参数保持不变:

// 万有引力常数
const G = 6.67259E-13;

再查看示例效果:

自由落体变得舒缓又平滑,似乎也再没有掉帧的感觉。

速度上限

上面使用基本物理常数对自由落体进行了模拟,并使用上帝之手修改了万有引力常数,这个二维物理世界已经看起来很自然了,但我们还是忽略了一点,那就是速度的上限。

在这个系统中,速度是通过加速度进行不断累积的,可以想象,如果物体的下落距离足够远,加速度的累加效果将非常明显,这意味着物体的速度将非常非常快。

速度过快会带来很多问题,对这个二维 “现实” 世界而言,过快的速度意味的被渲染的物体会在视觉上产生空间移位,这种效果就像游戏中使用 空间传送 技能一样,物体将从一个坐标直接跳到另一个坐标,视觉上并不友好。

基于此,我们需要对速度进行限制。

如果直接设置一个速度上限有点不符合物理规律,所以我们还是从现实世界入手,借助光速上限和相对论质量来处理速度上限。爱因斯坦给出了相对论中物体质量与速度的关系:

m = \frac{m_0}{\sqrt[2]{1 – \frac{v}{c}^2}}

其中 m_0 是物体的静止质量,m 是相对论质量,v 是物体速度,c 是光速。

在这个二维世界中,我们也可以定义自己的 光速 ,它将作为这个二维物理世界的速度上限。不过现实世界的光速上限对我们而言可能不够好。假如二维物理世界中一个像素表示显示世界的 1米 ,那么要想察觉到光速带来的变化,至少需要一个 3000万 像素宽的屏幕,这是不可能的。

所以需要重新定义光速,在这个二维物理世界中,光速应该被限制在 60m/s 。这个速度似乎非常小,但对这个世界而言直观上的感受会更好一点,因为显示屏的刷新速度一般是 60hz ,如果我们希望物体不会产生空间传送效果,那么最快的速度也就是每一帧只让这个物体移动一个像素。不过这个速度在视觉上还是太缓慢了,所以我们将其扩大十倍,这个二维世界的光速上限就定义为 600m/s

// 光速上限 600m/s ,屏幕刷新速度是 60hz ,所以 c = 600 / 60 = 10
const C = 10;

带速度上限的自由落体

基于上面的理论,重写 Mover 实现自由落体。

class Mover {
  // ...
  addForce(force) {
    const m = this.getRelativisticMass();
    // 相对论质量无穷大时,加速度趋近于 0 ,所以设置为 0
    const a = !isFinite(m) ? createVector(0, 0) : force.div(m);
    // 通过 limit 将计算值进行修正
    this.velocity.add(a).limit(C);
  }

  getRelativisticMass() {
    return this.mass / Math.sqrt(1 - Math.pow(this.velocity.mag() / C, 2));
  }
  // ...
}

其余代码保持不变,设置光速上限后的自由落体效果如下:

这个示例与上面的区别似乎不是很大,这没有关系,到目前为止我们已经处理了万有引力和速度上限,后续的文章中将尝试构造一个恒星系统,并基于万有引力构造粒子系统,并尝试在微观上模拟电磁力。

类似文章