Ogre中的四元数Quaternion与旋转

想象一个物体在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 );

即可实现该node饶X轴旋转-90度的效果。



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

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

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

mNode->rotate(quat);

SceneNode的getOrientation获得该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又会得到什么?我可以告诉你,第一句代码整体的作用就是获取该物体当前面向的方向。关于四元数与向量相乘,如图所示:

Ogre中的四元数Quaternion与旋转_第1张图片

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

那么第一句代码中为什么要乘上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中的四元数Quaternion与旋转)