Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

向量与变换

向量

在线性代数中,我们学习过,如果我们确定了一组线性无关的基,那么三维空间中的某个点坐标,可以使用一个向量来表示。一般而言,我们经常取一组空间相互正交的基,并令他们的模长为1,于是,我们的xyz三个轴的基向量分别表示为:。且相互之间两两做点积为0。

对于空间中的一个向量,利用我们的正交基,可以将其表示为即:

一般情况下,我们将其基的三个系数,成为a在该基下的坐标。即的坐标为

旋转

现在,假设我们有两组正交基,它们分别为 (世界空间基)和 (局部空间基)。同一个空间向量 在这两组基下的表示分别为:

于是,我们得到等式:

现在,我们假设第二组正交基(局部基)中的每一个向量,都可以通过第一组基(世界基)的线性组合来表示。即:

将其写成矩阵形式,我们得到了两组正交基的变换公式:

我们将这个中间的 矩阵记为 。将其带入之前的等式:

由于基向量是线性无关的,我们便得到了两组坐标的转换公式:

既然我们已经建立了等式 ,那么反过来,如果我们已知旋转后的向量在世界空间中的表示 ,想要推导它在局部空间(即相对于那组新基 )的坐标 ,该怎么办?

利用正交基的一个核心性质:向量与基向量的点积即为该向量在该方向上的分量投影。我们将等式两边同时与局部基向量做点积:

代入:

同理可得 。写成矩阵形式:

观察这个矩阵中的每一个元素。由于我们的基向量均为单位向量(模长为 1),根据点积的定义 ,每一个元素 实际上就是两组基向量之间夹角的余弦值

因此,这个矩阵被称为方向余弦矩阵。并且,他就是的逆矩阵

现在,我们将这个发现与之前的旋转矩阵 进行对比。你会发现一个极其优美的数学对称性:

  1. 从局部到世界:矩阵 是新基在旧基下的投影。
  2. 从世界到局部:上面这个投影矩阵的是新基在旧基下的投影。

这意味着,对于正交旋转矩阵,其逆矩阵等于其转置矩阵,这是一个很重要的性质!)。

小结:不管是如何进行旋转变换,我们总要找出一个旋转矩阵,然后用旋转矩阵和原来的坐标做左乘,即可得到新的旋转之后的坐标。在正交基的情况下,这个矩阵就是方向余弦阵,因此我们有

平移

相对于旋转,平移在直觉上要简单得多。在三维空间中,平移仅仅是将物体从一个点 移动到另一个点 ,其变换过程可以简单地表示为向量加法:

其中 是我们的平移向量。

World-to-Local

到目前为止,我们已经用 矩阵 解决了旋转和缩放。如果我们想对坐标系同时进行旋转和平移,那么我们的坐标变换公式会变成: 其中是原来坐标系下的坐标,是变换之后的坐标。

这个公式在数学上被称为仿射变换。然而,从计算机图形学和引擎设计的角度来看,这个公式不适合硬件加速运算:

  1. 无法合并:如果你有一连串的变换(比如父实体转了 30 度并移动了 5 米,子实体又转了 20 度),必须交替进行矩阵乘法和向量加法,这使得变换的复合变得极其复杂。
  2. 原点的“诅咒”:对于任何 矩阵 ,都有 。这意味着在线性变换的视角下,原点是被钉死的,你永远无法通过“乘法”把一个处于原点的物体挪走。

为了将平移这个“加法”操作统一进矩阵的“乘法”大门,我们需要引入齐次坐标。我们将三维向量 提升到四维,增加一个分量

通过构建一个 矩阵,可以将旋转 和平移 完美地融合在一起: 对于这个式子,我们把中间的矩阵记作如下,这个变换叫做 World-to-Local(世界坐标系到局部坐标系) 变换,其中是新的局部坐标系的原点,在世界的局部坐标系下的坐标。

当展开这个矩阵乘法时,你会发现:

  • 前三列与坐标相乘,完成了旋转。
  • 第四列与 相乘,恰好将平移分量 加到了结果中。

这说明,无论多么复杂的变换序列(移动、旋转、再移动、再缩放),在底层都可以坍缩为一连串 4x4 矩阵的连乘。 这也是为什么 GPU 专门针对 4x4 矩阵运算进行了硬件优化。

小结:如果假设点不动,而坐标系发生了旋转和平移,那么在新的坐标系下的坐标x’与x的关系为:

Local-to-World

在前面的讨论中,我们研究的是如何将世界坐标映射到新的局部坐标系中。但在构建场景时,我们更常见的操作是:定义一个物体在自己的局部空间中的样子,然后将其“放置”到世界空间中。

如果我们已知点在局部坐标系下的坐标 ,想要反求它在世界坐标系下的坐标 ,只需要对之前的等式 进行求逆操作:

我们将这个逆矩阵记为 。对于齐次变换矩阵 ,其逆矩阵具有非常特殊的结构。利用旋转矩阵的正交性(),我们可以直接写出这个矩阵:

注意:这里的 不再带转置,因为它代表的是局部基向量在世界空间下的方向。不仅仅是平移的负,而是旋转和平移整体作用后的负

在 Bevy 中,当你创建一个实体并设置它的 Transform 时,你本质上就是在定义这个 Local-to-World 矩阵:

  • 旋转(Rotation):填充矩阵左上角的 部分。
  • 平移(Translation):填充矩阵第四列的前三个元素。

点旋转时的变换

在上面,我们讨论的都是坐标系动、点不动的情况。现在,我们切换到另一个视角:坐标系 始终固定不变,而空间中的点(或向量)从 位置旋转到了 位置。

显而易见的,点相当于坐标系转换一个角度,等于坐标系**反向旋转(因此这里是R的逆的逆,即R本身)**一个相同的角度,因此我们可以直接得到。 对于齐次变换,同样可以容易得到: 除此之外,要注意!这里的是在原来的坐标系下的坐标,也就是说他和p是同一个坐标系下面的坐标。

上一小节的中的是坐标系在旋转后的,新的坐标系下面的,坐标。这很重要!很多教程往往不能正确区分他们。

几种情况下的计算方法

情况1:假设某点坐标为,其按照如下顺序进行了变换:首先相对于原始坐标系旋转了一个角度,变换矩阵为得到,然后又相对于原始坐标系再次旋转了一个角度,变换矩阵为,那么最终坐标的关系是什么?

注意⚠️!这里是点绕着坐标系旋转,而不是坐标系本身在变化

根据上一小节中的内容,经过第一次旋转后,点的位置变为: 此时, 是一个在原始坐标系下表达的新坐标值。

因为第二次旋转 依然是相对于原始坐标系定义的,它直接作用于当前空间中的任何向量。由于 此时已经在原始坐标系中就位,我们直接对其应用 将步骤 1 的等式代入步骤 2,利用矩阵乘法的结合律我们得到: 用处:这个公式解释了一个物体的中心点坐标应该如何在固定的世界坐标系中连续变换,只需要不断的左乘在原来的结果上即可。


情况2:假设某点坐标为,其坐标系按照如下顺序进行了变换:首先相对于原始坐标系旋转了一个角度,坐标系的变换矩阵为,然后又相对于原始坐标系再次旋转了一个角度,坐标系变换矩阵为,那么在最终的坐标系下的坐标的关系是什么?

注意⚠️!这里是坐标本身在旋转,而点没有发生变化

这个问题要稍微难一些。重新回顾我们前面的定义,如果有局部坐标下的坐标 ,想要反求它在世界坐标系下的坐标 ,我们有: 现在,我们来考察这两次变换后,最终的坐标系的基,在世界坐标系下的是什么。由于我们仅考虑旋转,因此平移向量是零向量。因此我们得到,每一次变换后,基的变换矩阵为: 根据情况1的结论,当一个点(向量)绕着定轴连续绝对变换时,基的最终坐标为 由于点没有发生变化,因此我们有: 因此我们得到(只有在是单位阵下才成立): 用处:这个公式解释了多个旋转如何被合并为一个旋转


情况3:假设某点坐标为,其坐标系按照如下顺序进行了变换:首先相对于原始坐标系旋转了一个角度,坐标系的变换矩阵为,然后又相对已经旋转后的新的坐标系再次旋转了一个角度,坐标系变换矩阵为,那么最终坐标的关系是什么?

在第一次旋转时,对于新坐标系下的坐标 在第二次旋转时,对于最终的坐标系系下的坐标 我们可以得到 用处:这个公式解释了如何相对自己目前的坐标来进行连续旋转,只需要不断的右乘在原来的变换矩阵上即可。


总结

这部分相对来说比较绕,记住一个口诀“外左内右”:

  • 生变换(绕固定世界轴):新矩阵往边乘。
  • 生变换(绕自身局部新轴):新矩阵往边乘。

四元数