一、前言
欧拉角与四元数都被用来处理图像学中的旋转。两者各有优劣,下面我们对它们做详细的比较。
二、欧拉角
欧拉角是表示朝向的最简单最直观方法,只需存储绕 X、Y、Z 轴旋转的角度,非常容易理解。你可以用vec3来存储一个欧拉角:
vec3 EulerAngles( RotationAroundXInRadians, RotationAroundYInRadians, RotationAroundZInRadians);
这三个旋转是依次施加的,通常的顺序是:Y-Z-X(但并非一定要按照这种顺序)。顺序不同,产生的结果也不同。
不过,面对更加复杂的情况时,欧拉角就显得力不从心了。例如:
- 对两个朝向进行插值比较困难。简单地对X、Y、Z角度进行插值得到的结果不太理想。
- 实施多次旋转很复杂且不精确:必须计算出最终的旋转矩阵,然后据此推测出欧拉角。
- “臭名昭著”的“万向节死锁”(Gimbal Lock)问题有时会让旋转“卡死”。其他一些奇异状态还会导致模型方向翻转。(参考视频:https://www.bilibili.com/video/av35803395/)
- 不同的角度可产生同样的旋转(例如-180°和180°)
- 容易出错——如上所述,一般的旋转顺序是YZX,如果用了非YZX顺序的库,就有麻烦了。
- 某些操作很复杂:如绕指定的轴旋转N角度。
三、四元数
四元数由4个数 [x y z w] 构成(这里我们不考虑数学上的定义,只考虑图像学方面的应用),RotationAxis 为旋转轴,RotationAngle 为旋转的角度。下图的旋转用四元数可表示为:
// RotationAngle is in radians
x = RotationAxis.x * sin(RotationAngle / 2)
y = RotationAxis.y * sin(RotationAngle / 2)
z = RotationAxis.z * sin(RotationAngle / 2)
w = cos(RotationAngle / 2)
1、四元数各值的意义
四元数的形式不如欧拉角直观,
x
y
z
xyz
xyz 分量大致代表了各个轴上的旋转分量,而
w
=
cos
(
R
o
t
a
t
i
o
n
A
n
g
l
e
/
2
)
w=\cos(RotationAngle/2)
w=cos(RotationAngle/2)。举个例子,假设你在调试器中看到了这样的值
[
0.7
0
0
0.7
]
[ 0.7\ 0\ 0\ 0.7 ]
[0.7 0 0 0.7]。
x
=
0.7
x=0.7
x=0.7,比
y
y
y、
z
z
z 的大,因此主要是在绕 X 轴旋转;而
2
arccos
(
0.7
)
=
1.59
2\arccos(0.7) = 1.59
2arccos(0.7)=1.59 弧度,所以旋转角度应该是
90
°
90°
90°。
同理,
[
0
0
0
1
]
(
w
=
1
)
[0\ 0\ 0\ 1] (w=1)
[0 0 0 1](w=1) 表示
R
o
t
a
t
i
o
n
A
n
g
l
e
=
2
arccos
(
1
)
=
0
RotationAngle = 2\arccos(1) = 0
RotationAngle=2arccos(1)=0,当旋转角度为
0
0
0 时因此这是一个单位四元数(unit quaternion),表示没有旋转。
2、四元数转换为旋转矩阵
3、参考源码
// Ogre 中的实现
mat4 ToRotationMatrix(quat& q)
{
mat4 matR;
float x = q.x;
float y = q.y;
float z = q.z;
float w = q.w;
float fTx = x + x;
float fTy = y + y;
float fTz = z + z;
float fTwx = fTx * w;
float fTwy = fTy * w;
float fTwz = fTz * w;
float fTxx = fTx * x;
float fTxy = fTy * x;
float fTxz = fTz * x;
float fTyy = fTy * y;
float fTyz = fTz * y;
float fTzz = fTz * z;
matR[0][0] = 1.0f - (fTyy + fTzz);
matR[0][1] = fTxy - fTwz;
matR[0][2] = fTxz + fTwy;
matR[0][3] = 0.0f;
matR[1][0] = fTxy + fTwz;
matR[1][1] = 1.0f - (fTxx + fTzz);
matR[1][2] = fTyz - fTwx;
matR[1][3] = 0.0f;
matR[2][0] = fTxz - fTwy;
matR[2][1] = fTyz + fTwx;
matR[2][2] = 1.0f - (fTxx + fTyy);
matR[2][3] = 0.0f;
matR[3][0] = 0.0f;
matR[3][1] = 0.0f;
matR[3][2] = 0.0f;
matR[3][3] = 1.0f;
return matR;
}
参考资料
[1] OpenGL 教程 旋转 : https://github.com/cybercser/OpenGL_3_3_Tutorial_Translation/blob/master/Tutorial%2017%20Rotations.md
[2] Ogre Quaternion :https://bitbucket.org/sinbad/ogre/src/3cbd67467fab3fef44d1b32bc42ccf4fb1ccfdd0/OgreMain/src/OgreQuaternion.cpp?at=default
欢迎关注个人公众号,实时推送最新博文!