矢量反射与粒子碰撞的问题

时间:2015-04-01 04:18:38

标签: javascript html5 math three.js

我想知道在我的粒子碰撞模拟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

3 个答案:

答案 0 :(得分:1)

首先,您似乎正在寻找的粒子碰撞是Elastic collisions,其中有数学计算覆盖碰撞后的速度计算。

碰撞在动量框架的中心最简单,所以如果你首先计算那个框架 V =(m 1 v 1 + m 2 v 2 )/(m 1 + m 2 ),然后你可以从两个粒子中减去它,做一个简单的对称碰撞,然后再加上帧速度。

从那里,使用该页面的公式in the 2 & 3d section计算速度。

代码的具体要点:

  1. pimpj = - pjmpi,因此您不需要两者
  2. 当最后一帧与此帧之间的路径过于接近时发生冲突;如果你只检查每一帧的距离,你就会遇到粒子高速飞过彼此的问题,并且你必须继续移动它们的位置,因为它们在你发现碰撞时已经重叠了。
  3. 理想情况下计算影响位置并使用它们重定向它们。
  4. 对于速度,只计算一次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-pjvi-vj为负的标量乘积。

如果没有它,你可以进入一个无限循环,当距离保持在临界半径以下时,速度会来回反射。