现在的位置: 首页 > 综合 > 正文

四元数

2014年04月05日 ⁄ 综合 ⁄ 共 5039字 ⁄ 字号 评论关闭

 

       想象一个物体在3D空间中移动的过程,该物体必然会涉及到旋转。例如一个怪物,他的运动方向会改变,要改变其方向只需要对其进行旋转即可。

       旋转的方式大致分为三种:Euler旋转,矩阵旋转,以及四元数旋转。

       这里稍微记录下我目前对于四元数旋转的理解。对于四元数方面的数学,以及其原理,这里不关心,只需要学会如何使用即可。

       无论是哪一种旋转,物体与该物体的局部坐标系之间的相对位置,相对方位都是不会改变的。因此,在进行两个局部旋转(即相对于局部坐标系)时,要注意结果可能不是你预期的。

       对于Euler旋转,OGRE中为SceneNode提供了yaw,
pitch, roll
之类的接口。这些接口默认都是参照局部坐标系旋转,可以通过第二个参数来指定,例如 yaw( Degree( 90 ), SceneNode::TS_WORLD );

       OGRE中的Quaternion类用于四元数处理。该类(也可以说是四元数本身)有四个成员:x,y,z,w。这四个数分别代表什么?

       OGRE论坛上我找到了一些可以让人很容易理解的信息:

       Quaternions can seem pretty daunting because of the use of 'imaginary' numbers. It's much easier to understand if you just ignore this concept completely. The basic formula for creating a quaternion
from angle/axis is:

 

Q = cos (angle/2) + i (x * sin(a/2)) + j (y * sin(a/2)) + k(z * sin(a/2))

 

or

Code:

 

Q.w = cos (angle / 2)

Q.x = axis.x * sin (angle / 2)

Q.y = axis.y * sin (angle / 2)

Q.z = axis.z * sin (angle / 2)

稍微忽略下那些复数之类的概念,使用角度/轴的方式创建四元数的公式为:

Q = cos (angle/2) + i (x * sin(a/2)) + j (y * sin(a/2)) + k(z * sin(a/2))

 

对应的代码为:

Q.w = cos (angle / 2)

Q.x = axis.x * sin (angle / 2)

Q.y = axis.y * sin (angle / 2)

Q.z = axis.z * sin (angle / 2)

 

再看一下OGRE中关于Quaternion的一个构造四元数的函数源代码:

void Quaternion::FromAngleAxis (const Radian& rfAngle,

        const Vector3& rkAxis)

{

    // assert:  axis[] is unit length

    //

    // The quaternion representing the rotation is

    //   q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)

 

    Radian fHalfAngle ( 0.5*rfAngle );

    Real fSin = Math::Sin(fHalfAngle);

    w = Math::Cos(fHalfAngle);

    x = fSin*rkAxis.x;

    y = fSin*rkAxis.y;

    z = fSin*rkAxis.z;

}

 

虽然可以说四元数中的w代表旋转量,x, y, z代表对应轴,但是这也不全正确。因为我们看到,对于真正的旋转量啊之类的数据,是需要进行有些公式变换后,才得到w, x, y,
z
的。

但是,即使如此,我们还是可以这样简单地构造一个四元数用于旋转:

     Quaternion q( Degree( -90 ), Vector3::UNIT_X );

该构造函数第一个参数指定旋转角度,第二个参数指定旋转轴(可能不是),上面的代码就表示,饶着X轴(正X方向),旋转-90度。将该四元数用于一个Scene
Node
旋转:

sceneNode->rotate( q );

即可实现该nodeX轴旋转-90度的效果。

 

再看一下OGRE tutorial中的一段代码:

Vector3 src = mNode->getOrientation() * Vector3::UNIT_X;

Ogre::Quaternion quat = src.getRotationTo(mDirection);

mNode->rotate(quat);

SceneNodegetOrientation获得该node的方位,用一个四元数来表示。什么是方位?这里我也不清楚,但是对于一个四元数,它这里表示的是一种旋转偏移,偏移于初始朝向。

OGRE论坛上有这么一段话:

The reason there's no other way to convert a quaternion to a vector is because a quaternion is relative. It has no direction.

With a direction (like a vector) you could say "face north east".

But with a quaternion, you say "face 45 degrees clockwise from whatever direction you are already facing" (very simplified example). Without knowing which way the object is already facing, a quaternion
is virtually meaningless with respect to orientation. So we just default it to some initial direction, like Unit Z, and make all orientations relative to that.

然后,getOrientation() * Vector3::UINT_X又会得到什么?我可以告诉你,第一句代码整体的作用就是获取该物体当前面向的方向。关于四元数与向量相乘,如图所示:

可以进一步看出,四元数表示了一个旋转偏移,它与一个向量相乘后就获得了另一个向量。该结果向量代表了这个旋转偏移所确定的方向。

那么第一句代码中为什么要乘上UNIT_X呢?因为这里所代表的物体的初始朝向就是正X方向。

第二句话由向量构造一个四元数,它表示,从当前的朝向,旋转到目的朝向所需要的一个四元数。第三句话就直接使用该四元数来旋转该node。但是有时候似乎旋转不正确(在我的实验中,我使用的模型其初始朝向是负Y方向,在初始化时我又将其饶着X轴旋转了负90度,后来旋转时就不正确了),这可以通过rotate( q, SceneNode::TS_WORLD)来矫正。(所以估计是之前旋转导致的错误)(有时候我在想,为什么对于所有物体的旋转之类的变换,都不直接参照于世界坐标系?因为我们最终看到的就是在世界坐标系中。)

注意,当旋转角度是180度时,这里就会出现错误,为了防止这种错误,可以这样做:

Vector3 src = mNode->getOrientation() * Vector3::UNIT_X;

if ((1.0f + src.dotProduct(mDirection)) < 0.0001f)

{

     mNode->yaw(Degree(180));

}

else

{

     Ogre::Quaternion quat = src.getRotationTo(mDirection);

     mNode->rotate(quat);

} // else

 

原因之类的可以参看OGRE tutorial。以上信息都是个人理解,行文也比较乱,主要是自己做的笔记,一些基本定义可以参看其他书籍。

 

2007-7-21

 

更深的理解:

一个四元数(Quaternion)其实描述了一个旋转轴和一个旋转角度。这个旋转轴和这个角度可以通过Quaternion::ToAngleAxis转换得到。当然也可以随意指定一个角度一个旋转轴来构造一个Quaternion。这个角度是相对于单位四元数而言的,也可以说是相对于物体的初始方向而言的。

当用一个四元数乘以一个向量时,实际上就是让该向量围绕着这个四元数所描述的旋转轴,转动这个四元数所描述的角度而得到的向量。

注意,对于这里的旋转轴而言,是有方向区别的。例如饶+x旋转90度和饶-x旋转90度就是不一样的。可以将其看做饶一个向量旋转,判别旋转正向只需要用右手握住向量,然后大拇指指向向量方向,四指弯曲的方向即为旋转正向。

 

 

相关参考:

http://www.ogre3d.org/wiki/index.php/Intermediate_Tutorial_1

http://www.ogre3d.org/phpBB2/viewtopic.php?t=24219&view=previous&sid=ce193664e1d3d7c4af509e6f4e2718c6
      http://www.ogre3d.org/phpBB2/viewtopic.php?t=27250&view=previous&sid=86f6055c4b749dab640588cf90d6f745

 http://www.ogre3d.org/phpBB2/viewtopic.php?p=238826

other infomations:

Euler rotations can be useful and are supported in Ogre's API. There are problems however.

One is that the order of operations is important. If you pitch an object 90 degrees, then yaw it 90 you get a different result than if yaw is done before pitch. Try it.

 

However to actually rotate an object in Ogre, you'll either need to convert it to an axis/angle

rotation, store the data in the matrix as an axis/angle and do the conversions yourself or will need to convert it to a quaternion.

 

       A quaternion is composed of four components: a vector with x, y, z coordinates and a w rotation.

 

        X, Y and Z describe a vector which is the axis of rotation.W is the amount of rotation around the vector axis.

 

        Multiplying a quaternion by a vector gives us a vector describing the rotational offset from that vector (a rotated vector).

 

 

        Vector3::getRotationTo(Vector3) returns a quaternion describing a relative rotation from one vector to the next.

 

       This orientation quaternion can be thought of as a rotational offset from an objects initial facing vector.

       getOrientation函数返回的四元数代表一个旋转偏移值,相对于物体初始朝向的方向

 

        物体参照局部坐标系旋转,无论如何旋转,它相对于该坐标系而言其方位还是没变化。

        物体相对于局部坐标旋转,只是旋转整个坐标系而已。

 You can think about Quaternions as a rotation around and axis with a given angle.

SceneNode::getOrientation returns a quaternion which describe a rotation around and axis/angle from the identity orientation

【上篇】
【下篇】

抱歉!评论已关闭.