RecursiveFrog 发表于 2021-12-26 16:32

图形学笔记三 复数 四元数

参考课程P4:
https://www.bilibili.com/video/BV1X7411F744?p=4

在闫大佬视频中提到了四元数,没有细讲。只是说四元数更多是为了计算旋转的差值。比如,先旋转15度,再旋转25度,那么平均值呢,并不是旋转20度。也就是说,旋转矩阵并不适合做差值运算,而四元数就可以解决。

不过想了解四元数,这其实是个复杂的概念,先了解一下虚数可能会更容易一些。

参考H264系列三 虚数的意义,看完后就明白,虚数表示了旋转。

首先,假设有一根数轴,上面有两个反向的点:+1和-1。这根数轴的正向部分,可以绕原点旋转。显然,逆时针旋转180度,+1就会变成-1。这相当于两次逆时针旋转90度。将"逆时针旋转90度"记为 i ,我们可以得到下面的关系式:i^2 = (-1)
这个式子很眼熟,它就是虚数的定义公式。所以,我们可以知道,虚数 i 就是逆时针旋转90度,i 不是一个数,而是一个旋转量。

然后就是3Blue1Brown的视频:

四元数的可视化
【官方双语】欧拉公式与初等群论

但是我看了一会儿,还是觉得有点理解困难。又在评论区找到这个链接:

Krsjt:我之前也写了一点关于四元数和三维旋转的东西,虽然采取的是完全不同的approach。如果你是因为计算机图形学或者机器人动力学之类的原因接触到四元数,希望这个能够帮助到你:https://krasjet.github.io/quaternion/

原文很长,我来摘抄https://krasjet.github.io/quaternion/部分原文,并加上一些自己的理解。如果打不开这个链接,可以使用https://krasjet.github.io/quaternion/quaternion.pdf直接下载这个pdf。
一、复数


在介绍四元数与 3D 旋转之间的关系之前,我们先来讨论一下复数(Complex Number)的一些性质以及它与 2D 旋转之间的关系.四元数的很多性质在很多层面上都与复数非常类似,所以理解复数的一些性质会对理解四元数非常有帮助.
1.定义


任意一个复数∈ C 都可以表示为=+的形式,其中 ,∈ R 而且^2 = 1.我们将称之为这个复数的实部(Real Part), 称之为这个复数的虚部(Imaginary Part).因为=+其实就是对于 {1, } 这个基(Basis)的线性组合(LinearCombination),我们也可以用向量来表示一个复数:


image.png

因为这个向量有两个元素,我们可以使用复平面上的一个点来表示一个复数.复平面的横坐标代表它的实部,纵坐标代表它的虚部:


image.png

2.复数乘法


如果有两个复数 1 =+ , 2 =+ ,我们可以使用分配律来计算它们的乘积
12 = ( + )( + )=+++ ^2
因为 ^2 = 1,这可以进一步化简为
12 =    ++ =   +( + )i
如果仔细观察你就能发现,复数相乘的结果其实也是一个矩阵与向量相乘的结果,也就是说:


image.png


我们可以发现,复数相乘这个运算,其实是与

这个矩阵所代表的变换是等价的(虽然复数与这个矩阵的关系远远不止这些,但是你只需要理解它所代表的变换与复数的乘法运算之间的关系就可以了).
3.复数相乘与 2D 旋转


如果我们对这个矩阵进行一些变形


image.png


image.png

所以可以进一步变形:


image.png

我们将原本的矩阵变形为了两个变换矩阵的复合,左边是个缩放,右边是个旋转。
注:看到这里如果不懂,可以往回看之前的线代系列。

哎,后边的又难又长,先放在这儿,以后有需要再学。毕竟要抓大放小嘛,时间是有限的。。。。
二、欧拉角


如何通俗地解释欧拉角?之后为何要引入四元数?
1.欧拉角的算法思想是什么


陌生的你来到了成都,站在盐市口茫然四顾,想知道春熙路怎么走?这个时候你选择了去问路,得到了两种回答:
往东经104°04′、北纬30°40′走右转后一直走

第一种回答,告诉了你春熙路的绝对坐标,可是很反人类啊!
第二种回答,告诉了你春熙路的相对坐标,很具有操作性。

欧拉角算法的思想就是采用的第二种回答的方式,优点在于很好理解。
2.具体实现步骤


有这么一副动图,清楚的表明了如何通过欧拉角来完成旋转


v2-35da80e428ca5750491ffd2770e187e1_720w.gif


image.png

图中有两组坐标:
xyz为全局坐标,保持不动XYZ为局部坐标,随着物体一起运动

旋转步骤如下:
物体绕全局的z 轴旋转 α角继续绕自己的 X轴(也就是图中的 n轴)旋转 β角最后绕自己的 z轴旋转 r角

可能你感到奇怪,为什么第一步是绕着全局坐标旋转?因为要和世界保持联系,否则就和世界完全没有关
系了。

很显然,按照不同的旋转步骤,旋转的结果是不一样的。就好比刚才问路的时候,回答你,“左转再右转”,和“右转再左转”,肯定到达的地点是不一样的。
三、万向锁


参考
万向锁的理解
Unity编辑器-Scene面板里的万向锁现象

万向锁产生的根本原因是绕三个轴的旋转不是同时进行的,想象一下我们旋转矩阵的推导是不是绕三个轴的旋转矩阵乘起来得到的,这三个旋转矩阵的摆放顺序不同,最后得到的旋转矩阵也是不相同的,因此,一般的系统都会有一个规定,比如unity来说,先绕y轴旋转,然后绕x轴旋转,最后是绕z轴的旋转。

先绕y轴旋转,然后绕x轴旋转,最后绕z轴旋转,这里的xyz是模型坐标系的三个轴,当绕x轴旋转的角度为90°的时候,就会出现万向锁的问题,我们实际测试一下看看是为什么。如下图所示,绿色为y轴,红色为x轴,蓝色为z轴:


image.png

当绕x轴旋转90°的时候,会发生什么呢?

image.png

没错,正如我们想象的,蓝色的z轴转下去了,这时候绕z轴旋转任意角度就完全等价于一开始绕y轴旋转了,因为它们已经重合(相反)了。这意味着,当绕x旋转90°的时候,绕y轴旋转就等价于绕z轴旋转了,丢失了一个维度的信息,这就是万向锁。

如果还不能理解,一定要记住旋转顺序,这是理解问题的关键所在,先绕y轴旋转,再按x轴旋转,再按z轴旋转。然后先绕y旋转任意角度,然后绕x旋转90度,然后绕z旋转,观察现象即可。
四、四元数的引入

1.如何通俗地解释欧拉角?之后为何要引入四元数?


欧拉角和四元数都能表示一个旋转,同样还有转换矩阵也能表示一个旋转,它们三者之间可以互相转换。具体公式百度可查。

欧拉角是给人看的,对控制系统也好分开单独处理。

四元数是给计算机用的,避免了大量三角函数运算和死锁问题。

转换矩阵是一个矩阵,主要是便于向量的转换计算。直接一个矩阵左乘原坐标系向量就得到了新坐标系的向量。
2.为什么Unity3d旋转默认采用了有万向节死锁的欧拉角,而不用四元数?


Unity的底层是通过四元数记录物体旋转的,并通过矩阵和四元数实现物体的旋转及插值。但在上层Unity提供了,向欧拉角进行转换输出,并能够通过欧拉角进设置物体旋转的功能。

这是由于相较矩阵和四元数,欧拉角是最接近人类直观思维的一种3D旋转表达模式,我是说你应该不想通过输入矩阵或者四元数来旋转物体吧...(除非你是神仙)

在底层通过矩阵/四元数,记录完成旋转,避免万向锁,但在上层提供欧拉角的转换输出和旋转设定,这应该说是当下大多数3D架构都普遍采用的一种模式。Unity就是这样做的,所以不要再说它用了什么屑欧拉角,那是为了照顾我们这些上层开发的凡人。
3.可视化理解四元数,愿你不再掉头发


四元数的一个最主要的应用就是表示旋转,它既是紧凑的,也没有奇异性。而旋转的其他表示方法各有优劣:
旋转矩阵:用九个数来表示三个自由度,矩阵中的每一列表示旋转后的单位向量方向,缺点是有冗余性,不紧凑。旋转向量:用一个旋转轴和一个旋转角来表示旋转,但是因为周期性,任何2nπ的旋转等价于没有旋转,具有奇异性。欧拉角:将旋转分解为三个分离的转角,常用在飞行器上,但因为万向锁问题(Gimbal Lock) 而同样具有奇异性。
4.四元数——基本概念


可能四元数的由来大家都看过很多遍。很久以前,一位老者坐在大桥边上,看着过往船只,突然灵光一闪,在桥边石碑上洋洋洒洒刻上几行大字,四元数诞生了!故事大家都爱听,那么为什么我们需要四元数?一种说法是解决向量乘法,我们知道向量之间乘法有内积和外积,但这两个运算均不完美,即不满足群的条件(当然四元数诞生的时候也还没有内积外积的说法)。那向量之间是否存在这样一个非常完美的乘法,于是三维空间无法解决的问题就映射到四维空间。这便是四元数诞生的契机。

那么问题又来了,既然四元数只是为了解决矩阵乘法,那为什么我们现在要用四元数进行旋转,甚至替代了欧拉角、轴角等形式?首先,四元数并不是生来为了解决三维旋转,而是它的性质非常有利于表达旋转信息(后面会详述),所以了解四元数的性质要先于了解四元数在旋转中的应用。至于四元数替代欧拉角等形式,就需要牵扯到一些别的知识点,我先罗列一下四元数相比其他形式的优点:
解决万向节死锁(Gimbal Lock)问题仅需存储4个浮点数,相比矩阵更加轻量四元数无论是求逆、串联等操作,相比矩阵更加高效

所以综合考虑,现在主流游戏或动画引擎都会以缩放向量+旋转四元数+平移向量的形式进行存储角色的运动数据。
五、应用


参考Cocos 3.x 四元数 rotateAroundLocal。
页: [1]
查看完整版本: 图形学笔记三 复数 四元数