【问题标题】:How can I offset a global directional force to be applied over a local axis?如何抵消要在局部轴上施加的全局方向力?
【发布时间】:2018-01-16 23:15:24
【问题描述】:

我想相对于对象的局部轴施加一个向前的力,但我使用的引擎只允许我在全局轴上施加一个力。

我可以作为四元数访问对象的全局旋转。但是,我不熟悉使用 quats(通常未经高级数学训练)。是否有足够的信息来抵消沿所需轴施加的力?怎么样?

例如,要在全球范围内前进,我会这样做:

this.entity.rigidbody.applyForce(0, 0, 5);

但要保持沿对象的局部轴施加的力,我需要根据对象的旋转 quat 以不同的方式沿轴分配施加的力,例如:

w:0.5785385966300964
x:0
y:-0.815654993057251
z:0

我研究了四元数试图弄清楚这一点,但观看有关它们是什么以及为什么使用它们的视频并没有帮助我弄清楚如何实际使用它们,甚至开始弄清楚如何应用这里需要的偏移量。

到目前为止,我尝试过的只是对如何做的猜测,但这是错误的:

Math.degrees = function(radians) {
    return radians * 180 / Math.PI;
};

//converted this from a python func on wikipedia, 
//not sure if it's working properly or not
function convertQuatToEuler(w, x, y, z){ 
    ysqr = y * y;
    t0 = 2 * (w * x + y * z);
    t1 = 1 - 2 * (x * x + ysqr);
    X = Math.degrees(Math.atan2(t0, t1));
    t2 = 2 * (w * y - z * x);
    t2 = (t2 >= 1) ? 1 : t2;
    t2 = (t2 < -1) ? -1 : t2;
    Y = Math.degrees(Math.asin(t2));
    t3 = 2 * (w * z + x * y);
    t4 = 1 - 2 * (ysqr + z * z);
    Z = Math.degrees(Math.atan2(t3, t4));
    console.log('converted', {w, x, y, z}, 'to', {X, Y, Z});
    return {X, Y, Z};
}

function applyGlobalShift(x, y, z, quat) {
    var euler = convertQuatToEuler(quat.w, quat.x, quat.y, quat.z);
    x = x - euler.X; // total guess
    y = y - euler.Y; // total guess
    z = z - euler.Z; // total guess
    console.log('converted', quat, 'to', [x, y, z]);
    return [x, y, z];
}

// represents the entity's current local rotation in space
var quat = {
    w:0.6310858726501465,
    x:0,
    y:-0.7757129669189453,
    z:0
}

console.log(applyGlobalShift(-5, 0, 0, quat));

不要嘲笑我对如何计算偏移量的可怕猜测:P 我知道它甚至不接近,但我的数学真的很差

【问题讨论】:

  • 查看转换quaternions to Euler angles 以获取对象的局部坐标轴。
  • @PatrickRoberts 是否可以在没有转换的情况下做到这一点?这是一个需要最大效率的场景(游戏代码,每刻都发生)。
  • 最简单,将力向量从局部坐标系转换为世界坐标系,并在全局中施加力。
  • @minorlogic 如果我知道该怎么做,我就不会问这个问题
  • 只需应用从本地到全局的转换的旋转部分。为了帮助它,我需要更多信息,你如何存储你的对象转换

标签: javascript algorithm quaternions


【解决方案1】:

四元数被用作欧拉角的替换。因此,您的方法违背了他们的目的。与其尝试使用欧拉角,不如利用四元数的属性。

  1. 一个四元数有 4 个分量、3 个向量分量和一个标量分量。

    q = x*i + y*j + z*k + w
    

    因此,一个四元数有一个向量部分x*i + y*j + z*k 和一个标量部分w。因此,向量是具有零标量或实分量的四元数。

  2. 请务必注意,向量乘以四元数是另一个向量。这可以通过使用rules of multiplication of quaternion basis elements 轻松证明(留给读者作为练习)。

  3. 四元数的逆只是它的共轭除以它的大小。四元数w + (x*i + y*j + z*k)的共轭就是w - (x*i + y*j + z*k),它的大小是sqrt(x*x + y*y + z*z + w*w)

向量的旋转只是通过将该向量绕轴旋转一个角度而获得的向量。旋转四元数表示这样的角度轴旋转,如here所示。

向量v 可以绕轴旋转,并通过旋转四元数q 所代表的角度共轭 v q。换句话说,

v' = q * v * inverse(q)

其中v' 是旋转向量,q * v * inverse(q)conjugation operation

由于四元数代表一个旋转,可以合理地假设它的大小为一,使得inverse(q) = q* 其中q*q 的共轭。

关于将q 分离为实数部分s 和向量部分u 并简化四元数运算(如图所示here),

v' = 2 * dot(u, v) * u + (s*s - dot(u, u)) * v + 2 * s * cross(u, v)

其中dot 返回两个向量的点积,cross 返回两个向量的叉积。

将上述内容放入(伪)代码中,

function rotate(v: vector3, q: quaternion4) -> vector3 {
    u = vector3(q.x, q.y, q.z)
    s = q.w
    return 2 * dot(u, v) * u + (s*s - dot(u, u)) * v + 2 * s * cross(u, v)
}

现在我们知道如何用四元数旋转向量,我们可以使用世界(全局)旋转四元数通过旋转四元数共轭局部方向来找到对应的世界方向(或轴)。

本地正向轴始终由0*i + 0*j + 1*k 给出。因此,要找到对象的世界正轴,必须将向量(0, 0, 1) 与世界旋转四元数共轭。

使用上面定义的函数,前向轴变为

forward = rotate(vector3(0, 0, 1), rotationQuaternion)

现在您有了世界前轴,沿它施加的力将只是世界前轴的标量倍数。

【讨论】:

  • 好吧,经过一番困惑(我的数学能力差)、研究和误解、思考和重新思考之后,我有一个问题:您的答案以解释如何获得世界正向轴,但你能用伪代码演示如何获得所有 3 个轴吗?
  • 我开始说,我想沿局部轴应用矢量[0, 0, 5](使对象向前移动),但要沿全局轴施加正确的力(我只能沿我的物理引擎中的全局轴),我的函数需要输出,例如(一个错误的):[0.3, 2, 1.5] 基于对象的局部向量所面对的任何东西(四元数)。
  • 我想我真的只是要求详细说明“现在你有了世界前轴,沿它施加的力将只是世界前轴的标量倍数。”
  • 如果你有一个沿着局部轴的力(比如(0, 0, 5)),那么你只需用世界旋转四元数旋转它,就可以沿着相应的世界轴获得相同的力。在示例中,世界太空部队将是rotate(vector3(0, 0, 5), rotationQuaternion)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-26
相关资源
最近更新 更多