代码之家  ›  专栏  ›  技术社区  ›  Ben Hymers

四元数绕轴旋转的分量

  •  36
  • Ben Hymers  · 技术社区  · 14 年前

    我很难找到关于这个话题的好消息。基本上,我想找到四元数旋转的分量,也就是绕给定的轴(不一定是X,Y或Z-任何任意单位向量)。有点像把四元数投影到向量上。所以如果我要求绕平行于四元数轴的轴旋转,我会得到同样的四元数。如果我要求绕一个与四元数轴正交的轴旋转,我会得到一个恒等式四元数。在两者之间。。。嗯,这就是我想知道的:)

    3 回复  |  直到 13 年前
        1
  •  20
  •   Jay Kominek    7 年前

    我花了一天的时间试图为动画编辑器找到完全相同的东西;下面是我是如何做到的:

    1. 取你想找到旋转方向的轴,找到一个与其正交的向量。
    2. 使用四元数旋转这个新向量。
    3. 这个投影向量和原始正交向量的点积的acos就是你的角度。

      public static float FindQuaternionTwist(Quaternion q, Vector3 axis)
      {
          axis.Normalize();
      
          // Get the plane the axis is a normal of
          Vector3 orthonormal1, orthonormal2;
          ExMath.FindOrthonormals(axis, out orthonormal1, out orthonormal2);
      
          Vector3 transformed = Vector3.Transform(orthonormal1, q);
      
          // Project transformed vector onto plane
          Vector3 flattened = transformed - (Vector3.Dot(transformed, axis) * axis);
          flattened.Normalize();
      
          // Get angle between original vector and projected transform to get angle around normal
          float a = (float)Math.Acos((double)Vector3.Dot(orthonormal1, flattened));
      
          return a;
      }
      

    以下是查找正交法线的代码,但是如果您只需要用于上述方法的代码,您可能会做得更好:

    private static Matrix OrthoX = Matrix.CreateRotationX(MathHelper.ToRadians(90));
    private static Matrix OrthoY = Matrix.CreateRotationY(MathHelper.ToRadians(90));
    
    public static void FindOrthonormals(Vector3 normal, out Vector3 orthonormal1, out Vector3 orthonormal2)
    {
        Vector3 w = Vector3.Transform(normal, OrthoX);
        float dot = Vector3.Dot(normal, w);
        if (Math.Abs(dot) > 0.6)
        {
            w = Vector3.Transform(normal, OrthoY);
        }
        w.Normalize();
    
        orthonormal1 = Vector3.Cross(normal, w);
        orthonormal1.Normalize();
        orthonormal2 = Vector3.Cross(normal, orthonormal1);
        orthonormal2.Normalize();
    }
    

    例如,如果你的四元数围绕X旋转90度,围绕Y旋转90度,你会发现如果你围绕Z分解旋转,它也将是90度。如果你想象一个向量做这些旋转,那么这是完全有意义的,但取决于你的应用程序,它可能不是你想要的行为。 对于我的应用-约束骨架关节-我最终得到了一个混合系统。矩阵/quat在整个过程中使用,但当谈到约束关节的方法时,我在内部使用了euler角,将旋转quat分解为每次围绕X、Y、Z的旋转。

        2
  •  35
  •   Eric    8 年前

    对于这个问题有一个优雅的解决方案,特别适用于四元数。它被称为“摇摆扭曲分解”:

    /**
       Decompose the rotation on to 2 parts.
       1. Twist - rotation around the "direction" vector
       2. Swing - rotation around axis that is perpendicular to "direction" vector
       The rotation can be composed back by 
       rotation = swing * twist
    
       has singularity in case of swing_rotation close to 180 degrees rotation.
       if the input quaternion is of non-unit length, the outputs are non-unit as well
       otherwise, outputs are both unit
    */
    inline void swing_twist_decomposition( const xxquaternion& rotation,
                                           const vector3&      direction,
                                           xxquaternion&       swing,
                                           xxquaternion&       twist)
    {
        vector3 ra( rotation.x, rotation.y, rotation.z ); // rotation axis
        vector3 p = projection( ra, direction ); // return projection v1 on to v2  (parallel component)
        twist.set( p.x, p.y, p.z, rotation.w );
        twist.normalize();
        swing = rotation * twist.conjugated();
    }
    

    这个代码的详细答案和推导可以在这里找到 http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/

        3
  •  1
  •   Kirill Belonogov    6 年前

    // We have some given data
    Quaternion rotation = ...;
    Vector3 directionAxis = ...;
    
    // Transform quaternion to angle-axis form
    rotation.ToAngleAxis(out float angle, out Vector3 rotationAxis);
    
    // Projection magnitude is what we found - a component of a quaternion rotation around an axis to some direction axis
    float proj = Vector3.Project(rotationAxis.normalized, directionAxis.normalized).magnitude;
    
        4
  •  1
  •   Zach Luke Hutchison    4 年前

    minorlogic的答案指出swing-twist分解是迄今为止最好的答案,但它缺少了一个重要的步骤。问题是(使用minorlogic的伪代码符号)如果 ra p twist ,使生成的旋转轴指向与 direction 颠倒 projection(ra, direction) . 请注意 投影(ra,方向) 计算点积,所以应该重用它,而不是计算两次。

    下面是我自己的swing twist投影版本(在某些情况下使用不同的变量名,而不是minorlogic的变量名),并进行了点积校正。代码是针对jomljdk库的,例如。 v.mul(a, new Vector3d()) 计算 a * v ,并将其存储在新的向量中,然后返回。

    /**
     * Use the swing-twist decomposition to get the component of a rotation
     * around the given axis.
     *
     * N.B. assumes direction is normalized (to save work in calculating projection).
     * 
     * @param rotation  The rotation.
     * @param direction The axis.
     * @return The component of rotation about the axis.
     */
    private static Quaterniond getRotationComponentAboutAxis(
                Quaterniond rotation, Vector3d direction) {
        Vector3d rotationAxis = new Vector3d(rotation.x, rotation.y, rotation.z);
        double dotProd = direction.dot(rotationAxis);
        // Shortcut calculation of `projection` requires `direction` to be normalized
        Vector3d projection = direction.mul(dotProd, new Vector3d());
        Quaterniond twist = new Quaterniond(
                projection.x, projection.y, projection.z, rotation.w).normalize();
        if (dotProd < 0.0) {
            // Ensure `twist` points towards `direction`
            twist.x = -twist.x;
            twist.y = -twist.y;
            twist.z = -twist.z;
            twist.w = -twist.w;
            // Rotation angle `twist.angle()` is now reliable
        }
        return twist;
    }
    
        5
  •  0
  •   Edward Andò    14 年前

    1. 取你想找到的旋转轴,然后找到一个 与之正交的向量。

    不足以得到可重复的结果。我已经在纸上发展了这一点,我建议采取以下行动,选择与“你想找到旋转轴”正交的向量,即观察轴。有一个与观察轴正交的平面。你必须把四元数的旋转轴投射到这个平面上。使用这个结果向量作为与观察轴正交的向量将得到很好的结果。

    感谢赛博为我定下了正确的方向。