夜光猪

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
转自:  Ogre Wiki
作者:  Cory Petkovsek
翻译肥羊
 
目录
·1简介
·2必备知识
       ·2.1向量(Vectors)
·3欧拉(Euler)旋转
·4矩阵(Matrix)旋转
·5四元数(Quaternion)旋转
·6问题
       ·6.1四元数
       ·6.2其它
·7代码片断
       ·7.1物体在XZ平面上的旋转
       ·7.2平滑旋转
       ·7.3重设方向
 
简介:
如果你在Ogre中处理完对象,发现它们不像你想象的那样旋转。那么,此时阅读这篇文章,大概会有些勉强。当我刚接触Ogre时,我用了很长的时间才理解了向量(vector)。在学习矩阵(matrix)时,那时我还不懂四元数,矩阵的某些知识点搞得我非常迷茫。这篇文章是针对Ogre中四元数旋转的实际运用,其中没有介绍相关的数学知识。
 
必备知识:
在学习四元数之前,让我们回顾一下欧拉角、矩阵和向量数学这些基础知识。在这篇文章和Ogre中,我们使用的都是右手坐标系。这意味着两件事:其一,我们认为+X轴指向屏幕的正右方,+Y轴指向屏幕的正上方,+Z轴透过屏幕垂直指向你。其二,要查+45度角是如何旋转的,将你的右手拇指沿旋转轴方向伸出,其余手指的自然弯曲方向就是实际的角度旋转方向。
向量:
在数学里,一个向量描述了在3D空间中从一点指向另一点的一条有方向的线段。它具有方向和距离。这说明它并不是一般意义上的没有方向的线段(译者注:线段没有方向)也不是一条无限的射线(译者注:射线没有大小)。向量具有射线与线段的共同特征。(译者注:向量是一个有大小由方向的量。)
在3D空间中描述两个点需要六个参数。在Ogre中,向量的存储是假设第一个点为(0,0,0)第二个点为一个相关偏移量。这是向量的一个通常用法。可以用(0,0,0)作为世界的根,或者将根设定于一个对象的物体坐标系中。当进行变换(transformations)时只要知道你工作在那个坐标体系下就可以了。由于向量只有三个参数,所以经常被用来定义三位点。
长度为1的向量称为规格化向量。当我们仅仅要描述一个方向时可以使用单位向量或规格化向量。如果你期望的结果是一个需要通过许多精确的运算时,使用规格化限量是必须的。不要忘记规格化你的向量!
在Ogre中,向量的普遍用法是描述一个单一的点、使用单位向量定义一个方向或者计算两点之间的距离。
下面是一些有用的向量操作
·     向量B减去向量A
得到一个包含从A到B的距离和方向的向量(在这个例子中,A可以是(0,0,0),表示B的向量可以按A的自身的物体坐标系定义,而不是世界坐标系)。
即:Bv – Av = Av ->Bv 
·     向量A加上向量B
向量A加上向量B得到向量C。如果向量A与向量B首尾相接,就好像两个箭头依次指出了一个路径,向量C由向量A的尾部指向B的首部,构成一个三角形。
       ·乘上或除以一个标量
乘上或除以一个标量并不改变向量的方向,只是改变此向量的大小。例如,Vector(1,1,1)描述了一条(或者一个默认原点的3D点)与XZ轴平面正向夹角45度的线(向上,位与+X,+Y,+Z三个轴所围空间中),与XY平面45度(面向你),与YZ平面45度(向右)。其长度为sqrt(X^2+Y^2+Z^2)或者sqrt(3)。如果我们将向量乘以2,得到(2,2,2),构成了一个相同方向的向量。详情参阅Ogre API的Vector3::length()。
       ·向量的叉乘
两个向量叉乘得到一个与两个向量都垂直的向量。这是一个非常有用的操作。如果你要指出垂直于一个多边形的向量,你可以叉乘多边形的两个边,那么你就能够得到一个垂直于这个多边形平面的向量。详情参阅Ogre API的Vector3 Vector3::crossProduct(Vector3)。
       ·向量的点乘
两个向量点乘能得到它们的夹角。计算如下:DP(V1, V2) = (Cos Th) * Len1 * Len2,Len1和Len2是向量的大小。尽管如此,你也最好将向量都规格化,这可以使它们的大小都为1。规格化后的点乘计算:Th = ArcCos(V1, V2); Len1=Len2=1。详情参阅Ogre API的Real Vector3::dotProduct(Vector3)。
欧拉旋转:
我们最常用的旋转方法应该是使用yaw, roll和pitch。yaw是在XZ轴平面上围绕Y轴左右旋转,当开车时使用的是yaw。pitch在YZ轴平面上围绕X轴上下旋转,喷气机飞行或爬坡时用pitch向上或向下。roll是在XY轴平面上绕Z轴倾斜旋转,从字面意思上说,当你驾驶汽车高速急转弯时,你的汽车会出现roll运动:P
欧拉旋转十分的有用,Ogre API也提供了欧拉旋转。但是,旋转时也会出问题。一个是操作的顺序不能乱。如果你将对象先pitch 90度,然后再yaw 45度得到的结果肯定与先yaw  90度再pitch 45度不同。另外一个叫做万向节锁。万向节是一个旋转轴。这个问题发生在两个旋转轴重合时,此时需要删掉一个。
(译者注:欧拉角最著名的问题是这样的:当你先yaw 45度再pitch 90度,和先pitch90度再roll 90度是等价的。事实上,一旦先择±90度为pitch角,就被限定在只能绕竖轴旋转。这种现象,角度为±90度的第二次旋转使得第一次和第三次旋转轴相同,成为万向锁。为了消除限制欧拉角的这种别名现象,规定万向锁情况下只能由yaw完成绕竖轴的旋转。即若pitch = ±90度,则roll = 0)
总的来说,欧拉角在实际的应用是受限的。这里我所指的是使用yaw, roll, pitch操作让对象任意旋转并不是很实用。如果你要一个可以任意pitch / yaw的相机(就像许多第一人称视角射击游戏)的话,它们是很有用的。使用双场景节点的技巧,你可以得到一个仅用yaw和pitch操作却能实现你所需要的全部旋转功能的相机。尽管如此,当你的一个对象面对一个方向,而且你想让它面对处于任意位置的对象时,取当前的yaw, roll和pitch值后再计算与新方向之间的角度差并不是十分有效的。同样的,如何使旋转更加平滑?进入矩阵的学习。
矩阵旋转:
由于使用任意三围欧拉旋转会产生大量的操作,通常使用矩阵来存储和转换它们。
我们使用三个矩阵来分别表示来自X, Y, Z轴的角偏移,即我们上述的pitch (围绕X轴), yaw(围绕Y轴)和roll(围绕Z轴)。我们将其存入独立的矩阵: matRotationX, matRotationY, matRotationZ。当我们确定我们要旋转的角度,并且将其存储到矩阵中。我们可以通过矩阵乘法将大量的旋转操作合并为一个简单的矩阵转换操作:
matTransform = matRotationX * matRotationY * matRotationZ;
幸运的是,Ogre提供Matrix3类来操作3X3的矩阵。这个类中也包含有*操作,因此上面的那个语句将在你的代码中良好的工作。不过,在Ogre中要真正旋转一个对象,你可以将其转换为轴/角旋转存储数据到矩阵自己做变换或者转换为四元数。
       注意,矩阵的乘法是不可交换的。A*B != B*A。矩阵旋转的顺序都是很重要的,就像欧拉角一样,不能随意变化。另外,以矩阵形式表现的欧拉角依然受到万向锁的影响。
       矩阵也可以用来操作轴/角旋转。上述矩阵使用了yaw, roll, pitch的概念。试想一下要是这些换为分别绕X, Y和Z轴的旋转,这将实现所有的轴/角旋转。通过这个我们避免了万向锁问题(尚未弄明白)。Ogre尚未提供旋转矩阵与四元数之间的转换。因此如果你要使用旋转矩阵,你将需要自己手动将其转为四元数。
       欧拉角和矩阵以及旋转矩阵有个共同的问题,叫做矩阵偏移,这将在下一部分叙述。
四元数旋转:
对于初学者来说,四元数是一个相当有趣的概念。正如上面描述的那样,当使用四元数时,我发现停止用yaw, pitch和roll思考而改为用绕轴的旋转来思考变得很容易。例如,我们要模仿一架喷气机上的推进器,喷气机面向Z轴。在欧拉角中,这个旋转应该被称作roll。在四元数中,其为绕一指向Z轴的向量旋转或绕Vector3::UNIT_Z旋转。
四元数由四部分组成: 一个具有x,y,z坐标和w旋转分量的向量。这是我在矩阵那一节提到的轴/角的表示方法。即使使用假设的参数,四元数数学也是相当棘手的。幸运的是,我们不需要从数学的角度使用它,也不需要了解它为什么要解释成旋转。来做个试验,首先竖起你左手的食指,然后用你的右手握住笔的一头。现在用你手腕和手做圆周运动,尽可能水平的旋转笔,始终保持其垂直于你左手的手指、平行于地面,就好像你把笔放在桌子上旋转一样。将这个想象成为绕Vector3::UNIT_Y旋转的四元数。
这时我们有一个从(0,0,0)到(1,1,-1)的向量。用刚才的手指向右指、保持与地面45度夹角。现在,围绕你的手指旋转笔,始终保持笔与手指垂直。注意我们如何通过角与轴的选择创建了一个不同的旋转。
像矩阵一样,我们也可以使用四元数相乘来计算之间的角度。不过四元数相乘仍然是不可交换的。Q1*Q2 != Q2*Q1。适用的顺序依然很重要。四元数也避免了万向锁问题。
 
四元数的优点
四元数有许多矩阵没有的优点。不过它依然遵循像数一样的正、负、乘法或加法等操作。按照实践得到的结论,优点如下:
       ·在轴/角表示中避免了万向锁。
       ·修改旋转更加容易。
假设我们描述一个-45度轴向左yaw旋转,创建一个新的四元数描述10度轴yaw向右旋转。将两个四元数相乘,我们拥有了一个-35度轴的旋转。这可以使用在其它对象或多个相同的对象需要实现相同旋转的情况。旋转因子的变化取决于环境。
       ·避免了耗费操作的矩阵偏移
当在矩阵上使用计算机中有限的点精确度执行大量的操作时,会发生矩阵偏移。四舍五入掉的实数逐渐增大,将会不可避免的弄坏矩阵。矩阵偏移将会导致异常的旋转的出现。必须把矩阵规格化才能重置旋转,这将非常耗费操作。从另一方面来说,四元数也有类似的痛苦的偏移,不过,由9个或更多的参数操作降至4个,这比规格化省力多了。
       ·增值旋转
当旋转对象时,我们希望对象旋转进行的平缓。我们常说“从你当前位置转到这个点。但是要使用300帧来实现它,每次移动整个角度的1/300。”矩阵的增值是可能的,但是别的方式可以么?四元数提供了两种增值方法: 线的(linear)和三次的(cubic)。Ogre提供了三个执行方法: slerp, nlerp和squad。一次增值允许两个四元数间增加的百分比是指定的。线性指的是运动的速率是固定的,如果使用相机或节点会感觉到是急动的。slerp和nlerp都是线性的,slerp更精确但更慢些。squad或cubic增值允许一次性指定四个四元数a, p, q, b。p, q用来定义点a,b间的增值曲线。它允许我们在增值时缓慢的增大速度,停止,减少速度。
       ·Ogre使用四元数
              这是应该你愿读这篇文章的原因吧:)
 
       一些有用的四元数
             
W
X
Y
Z
描述
1
0
0
0
恒定四元数,不旋转
0
1
0
0
绕X轴旋转180度
0
0
1
0
绕Y轴旋转180度
0
0
0
1
绕Z轴旋转180度
sqrt(0.5)
sqrt(0.5)
0
0
绕X轴旋转90度
sqrt(0.5)
0
sqrt(0.5)
0
绕Y轴旋转90度
sqrt(0.5)
0
0
sqrt(0.5)
绕Z轴旋转90度
sqrt(0.5)
-sqrt(0.5)
0
0
绕X轴旋转-90度
sqrt(0.5)
0
-sqrt(0.5)
0
绕Y轴旋转-90度
sqrt(0.5)
0
0
-sqrt(0.5)
绕Z轴旋转-90度
posted on 2013-04-18 16:15  夜光猪  阅读(1074)  评论(0编辑  收藏  举报