首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在两个四元数之间学习?

如何在两个四元数之间学习?
EN

Stack Overflow用户
提问于 2017-09-11 13:28:18
回答 4查看 1.3K关注 0票数 1

我有两个四元数:

代码语言:javascript
复制
SCNVector4(x: -0.554488897, y: -0.602368534, z: 0.57419008, w: 2.0878818) 

SCNVector4(x: 0.55016619, y: 0.604441643, z: -0.576166153, w: 4.18851328)

如果我们创建两个对象,方向将是非常相似的

但是,如果我们尝试从第一次到第二阶段进行Lerp,那么这个位置发生了很奇怪的变化(并且查看了预期的值,但不正确)

Lerp进度演示

我在googled上搜索并找到了许多函数来执行lerp,例如简单的一个:

代码语言:javascript
复制
extension SCNVector4 {

    func lerp(to: SCNVector4, v: Float) -> SCNVector4 {

        let aX = x + (to.x - x) * v
        let aY = y + (to.y - y) * v
        let aZ = z + (to.z - z) * v
        let aW = w + (to.w - w) * v
        
        return SCNVector4Make(aX, aY, aZ, aW)
        
    }
}

但是怎样才能避免这种奇怪的翻转呢?

PS:我尝试过与GLKit不同的函数,但结果是相同的1:https://i.stack.imgur.com/8jEvm.png

  • 正如建议的那样,试图翻转符号,但问题是,我得到的点乘积大于0。
代码语言:javascript
复制
    extension SCNVector4 {
    
    func glk() -> GLKQuaternion {
        return GLKQuaternion(q: (x, y, z, w))
    }
    
    func lerp(to: SCNVector4, v: Float) -> SCNVector4 {
        
        let a = GLKQuaternionNormalize(glk())
        let b = GLKQuaternionNormalize(to.glk())
        
        let dot =
            a.x * b.x +
            a.y * b.y +
            a.z * b.z +
            a.w * b.w
        
        var target = b
        if dot < 0 {
            target = GLKQuaternionInvert(b)
        }
        
        let norm = GLKQuaternionNormalize(GLKQuaternionSlerp(a, target, v))
        
        return norm.scn()
        
    }
    
}

extension GLKQuaternion {
    
    func scn() -> SCNVector4 {
        return SCNVector4Make(x, y, z, w)
    }
    
}
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-09-13 00:24:51

如果你问我,你列出的quat值似乎是错的。“w”值为2或4并不等于标准化的四元数,所以我并不奇怪,用它们来表示奇数值。当使用quats进行旋转时,它们应该是单位长度(而这两个quats不是单位长度)。

至于lerping,您基本上希望使用规范化的lerp (nlerp)或球形lerp (slerp)。当您从一个quat旋转到另一个quat时,NLerp会导致轻微的加/减速。Slerp给出一个恒定的角速度(虽然它使用正弦,所以计算速度更慢)。

代码语言:javascript
复制
float dot(quat a, quat b)
{
  return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
}

quat negate(quat a)
{
  return quat(-a.x, -a.y, -a.z, -a.w);
}

quat normalise(quat a)
{
  float l = 1.0f / std::sqrt(dot(a, a));
  return quat(l*a.x, l*a.y, l*a.z, l*a.w);
}

quat lerp(quat a, quat b, float t) 
{
  // negate second quat if dot product is negative
  const float l2 = dot(a, b);
  if(l2 < 0.0f) 
  {
    b = negate(b);
  }
  quat c;
  // c = a + t(b - a)  -->   c = a - t(a - b)
  // the latter is slightly better on x64
  c.x = a.x - t*(a.x - b.x);
  c.y = a.y - t*(a.y - b.y);
  c.z = a.z - t*(a.z - b.z);
  c.w = a.w - t*(a.w - b.w);
  return c;
}

// this is the method you want
quat nlerp(quat a, quat b, float t) 
{
  return normalise(lerp(a, b, t));
}

/Edit

你确定它们是quat值吗?如果你问我,这些值看起来很像轴角值。尝试通过这个转换函数运行这些值,看看这是否有帮助:

代码语言:javascript
复制
quat fromAxisAngle(quat q)
{
  float ha = q.w * 0.5f;
  float sha = std::sin(ha);
  float cha = std::cos(ha);
  return quat(sha * q.x, sha * q.y, sha * q.z, cha);
}

我从您的原始值中得到这两个生成的四元数:

(-0.479296037597,-0.520682836178,0.496325592199,0.50281768624)

(0.47649598094、0.523503659143、-0.499014409188、-0.499880083257)

票数 2
EN

Stack Overflow用户

发布于 2017-09-11 13:39:42

最新的SDK有<simd/quaternion.h>,它公开simd_quatf类型以表示四元数。此外,还公开了处理四元数的不同用途,其中包括simd_slerp,它为四元数插值做了“正确的事情”。

在iOS 11和macOS 10.13中,SceneKit公开了直接处理SIMD类型的新API。例如,除了SCNNode.orientation之外,您现在还可以访问SCNNode.simdOrientation

编辑

大多数SIMD都是内联的,因此可以比SDK版本更早地在OS版本上使用。如果你真的想坚持GLKit,他们的球面插值的版本是GLKQuaternionSlerp

票数 0
EN

Stack Overflow用户

发布于 2017-09-11 15:56:44

  1. 做签名翻转,如果四元数点乘积为负数。
  2. 标准化产生的四元数。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46156903

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档