我想知道在我的粒子碰撞模拟here中是否犯了数学错误。
在碰撞解决过程中,粒子似乎没有正确分离。这是函数的代码片段,它分隔粒子并改变它们的速度:
//particle 1
var pi = particles[i];
//particle 2
var pj = particles[j];
//particle 1 to particle 2
var pimpj = pi.mesh.position.clone().sub(pj.mesh.position);
//particle 2 to particle 1
var pjmpi = pj.mesh.position.clone().sub(pi.mesh.position);
//if colliding (radius is 20)
if(pimpj.length() < 20 && pimpj.length() != 0)
{
//reflect velocity off of 1->2
pi.velocity = pi.velocity.reflect(pimpj.clone().normalize()).multiplyScalar(restitution);
//reflect velocity off of 2->1
pj.velocity = pj.velocity.reflect(pjmpi.clone().normalize()).multiplyScalar(restitution);
//move particle 1 to appropiate location based off of distance in between
var pip = pi.velocity.clone().normalize().multiplyScalar(20-pimpj.length());
//move particle 2
var pjp = pj.velocity.clone().normalize().multiplyScalar(20-pimpj.length());
pi.mesh.position.add(pip);
pj.mesh.position.add(pjp);
}
我尝试在改变pi.velocity的同时用pjmpi反转pimpj,但没有效果。
注意:我正在使用 three.js
答案 0 :(得分:1)
首先,您似乎正在寻找的粒子碰撞是Elastic collisions,其中有数学计算覆盖碰撞后的速度计算。
碰撞在动量框架的中心最简单,所以如果你首先计算那个框架 V =(m 1 v 1 + m 2 v 2 )/(m 1 + m 2 ),然后你可以从两个粒子中减去它,做一个简单的对称碰撞,然后再加上帧速度。
从那里,使用该页面的公式in the 2 & 3d section计算速度。
代码的具体要点:
pimpj
= - pjmpi
,因此您不需要两者pimpj.clone().normalize()
并存储它 - 您之后不会更改此方向单位向量,因此您无需重新计算它,或计算pjmpi派生的等效项(请参阅# 1)答案 1 :(得分:0)
我认为问题的一部分是你的碰撞模型过于简单化了。碰撞的基本规则是动量守恒和能量守恒。看一维案例。如果两个粒子具有相同的质量m并且u1和u2是你预先的速度和v1,那么v2是之后的速度
m u1 + m2 u2 = m v1 + m v2
为完美碰撞提供能量守恒
1/2 m u1.u1 + 1/2 m u2.u2 = 1/2 m v1.v1 + 1/2 m v2.v2.
这两个方程的解v1 = u2,v2 = u1。那就是速度切换。特别是如果在碰撞之前一个速度为零,那么在碰撞之后另一个速度在碰撞之后变为零。你可以在使用牛顿摇篮时看到这种情况。
在2D中,我们可以在一个坐标系中解析,该坐标系沿着一个方向沿着接触平面和一个垂直于它的方向。力只发生在垂直方向,这意味着沿着窗格的速度不会发生变化,但垂直速度会发生变化。
var u = pjmpi.clone().normalize();
var v = new THREE.Vector3(u.y,-u.x,0);
// resolve in two directions
var piu = pi.velocity.dot(u);
var piv = pi.velocity.dot(v);
pi.velocity = new THREE.Vector3(
pju * u.x + piv * v.x,
pju * u.y + piv * v.y,
0);
pj.velocity = new THREE.Vector3(
piu * u.x + pjv * v.x,
piu * u.y + pjv * v.y,
0);
这适用于完美的弹性碰撞。请参阅Wikipedia elastic collision,其中有一个很好的插图。如果你把质量相等,那么最后的公式会简化一点。
对于与恢复R的部分非弹性碰撞,我们可以查看http://www.plasmaphysics.org.uk/collision2d.htm的结尾。现在采用质心w的速度。碰撞后这不会改变,因为总动量是守恒的。所以w =(u1 + u2)/ 2 =(v1 + v2)/ 2。取相对于质心的速度 V1&#39; = v1-w,v2&#39; = v2-w,应用恢复原状v1&#39;&#39; = R(v1&#39; -w),v2&#39;&#39; = R(v2&#39; -w)并加上质心的速度。
v1''' = R(v1-v2)/2 + (v1+v2)/2
v2''' = R(v1-v2)/2 + (v1+v2)/2
另请参阅维基百科Inelastic collision,它在1D中具有相同的公式。
这将代码转换为
var u = pjmpi.clone().normalize();
var v = new THREE.Vector3(u.y,-u.x,0);
// resolve in two directions
var piu = pi.velocity.dot(u);
var piv = pi.velocity.dot(v);
var pju = pj.velocity.dot(u);
var pjv = pj.velocity.dot(v);
// velocities after collision
var v1x = pju * u.x + piv * v.x;
var v1y = pju * u.y + piv * v.y;
var v2x = piu * u.x + pjv * v.x;
var v2y = piu * u.y + pjv * v.y;
// vel center of mass
var wx = (v1x+v2x)/2;
var wy = (v1y+v2y)/2;
// difference
var dx = (v1x-v2x)/2;
var dy = (v1y-v2y)/2;
// final velocities
pi.velocity = new THREE.Vector3(
wx + restitution * dx,
wy + restitution * dy,
0);
pj.velocity = new THREE.Vector3(
wx - restitution * dx,
wy - restitution * dy,
0);
// We can print the KE and momentum before and after to check
console.log("KE before ",
pi.velocity.lengthSq()+pj.velocity.lengthSq());
console.log("M before ",
pi.velocity.x+pj.velocity.x ,
pi.velocity.y+pj.velocity.y);
console.log("KE after",v1x*v1x+v1y*v1y + v2x*v2x + v2y*v2y);
console.log("M after ", v1x+v2x, v1y+v2y);
console.log("KE rest",
pi.velocity.lengthSq()+pj.velocity.lengthSq());
console.log("M rest ",
pi.velocity.x+pj.velocity.x ,
pi.velocity.y+pj.velocity.y);
这可以很好地简化。首先取两个粒子的平均值和一半差值。反映差异并应用恢复原状
var len = pjmpi.length();
// unit vector normal to plane of collision
var nx = pjmpi.x / len;
var ny = pjmpi.y / len;
// unit vector tangent to plane of collision
var tx = -ny;
var ty = nx;
// center of mass
var wx = (pi.velocity.x+pj.velocity.x)/2;
var wy = (pi.velocity.y+pj.velocity.y)/2;
// half difference
var dx = (pi.velocity.x-pj.velocity.x)/2;
var dy = (pi.velocity.y-pj.velocity.y)/2;
// resolve in two directions
var a = dx * nx + dy * ny;
var b = dx * tx + dy * ty;
// reflect difference in normal
var cx = -a * nx + b * tx;
var cy = -a * ny + b * ty;
// apply restitution and add back center of mass
pi.velocity.set(
wx + restitution * cx,
wy + restitution * cy,
0);
pj.velocity.set(
wx - restitution * cx,
wy - restitution * cy,
0);
我尽可能少地使用THREE以避免创建太多对象。
我已将此作为小提琴保存在http://jsfiddle.net/SalixAlba/8axnL59k/。我减少了点数,并删除了重力,使事情变得更加简单。
答案 2 :(得分:0)
除了其他答案中非常好的点之外,如果粒子朝向彼此移动,您只想进行速度变化。这是距离的导数为负的情况,可以转换为pi-pj
和vi-vj
为负的标量乘积。
如果没有它,你可以进入一个无限循环,当距离保持在临界半径以下时,速度会来回反射。