四元数与欧拉角

一、前言

欧拉角与四元数都被用来处理图像学中的旋转。两者各有优劣,下面我们对它们做详细的比较。

二、欧拉角

欧拉角是表示朝向的最简单最直观方法,只需存储绕 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

欢迎关注个人公众号,实时推送最新博文!
在这里插入图片描述

  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值