前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >变换(Transform)(1)-向量、矩阵、坐标系与基本变换

变换(Transform)(1)-向量、矩阵、坐标系与基本变换

原创
作者头像
Zero Two
发布2024-07-21 21:59:54
130
发布2024-07-21 21:59:54
举报
文章被收录于专栏:Real-Time Rendering

我也是一个学习者,有问题请指出🙏 Reference: 《Unity Shader 入门精要》 《Games101》课程 《Real-Time Rendering 4th editon》

左手系与右手系

众所周知,OpenGL与DirectX使用了不同的屏幕空间坐标系

如果要将右侧坐标系变为左侧那种,我们只需要做一些旋转操作,将右侧坐标系顺时针旋转180度,再将整个坐标系水平翻转即可。我们可以通过一定的旋转操作将两个坐标系重合,那么我们就称它们具有相同的旋向性(handedness)。

但对于三维坐标系来说,有时单靠旋转不能将两个坐标系重合,比如左手系与右手系。

除了坐标轴的朝向不同外,它们对于正向旋转的定义也不同,分别由左手法则与右手法则定义。一般来说,左手系的旋转正方向是顺时针,而右手系是逆时针。

左右手系之间可以进行相互转换,只需要让任意一轴反转,其他轴保持不变即可。

对于开发者来说,使用左手系和右手系都是一样的,不会影响底层的数学运算,只会在视觉上有一些差别。左右手坐标系在z轴上的移动以及旋转方向是不同的,如果要从一种坐标系转移到另一种坐标系,并保持视觉上的不变,则需要进行一些转换。

Unity中,模型空间和世界空间使用左手系;对于观察空间,则是右手系;对于观察空间,我们目视屏幕的方向一定是z轴,我们的右手边是x轴正方向;右手系则代表着z轴正方向是从屏幕指向了我们,z值越小代表着深度越大,离屏幕越远。

向量

向量的点积运算

向量的点积运算可以用来判断两个向量的方向

  1. 如果两个向量的点积大于0,则它们的夹角小于90度,即它们的方向趋于一致。
  2. 如果两个向量的点积小于0,则它们的夹角大于90度,即它们的方向趋于相反。
  3. 如果两个向量的点积等于0,则它们的夹角等于90度,即它们是正交(垂直)的。

向量的叉积运算

注意运算结果的方向与坐标系的类型有关系,如果是左手系需要用左手定则判断结果向量的方向,右手系则要用右手定则。

叉积一个很常见的应用则是判断一个点是否在三角形内部:

  1. 确定三角形的三个顶点坐标,分别记为 P1(x1, y1, z1), P2(x2, y2, z2), P3(x3, y3, z3)
  2. 计算三角形的三条边向量:
  • 边向量1:V1 = P2 - P1
  • 边向量2:V2 = P3 - P2
  • 边向量3: V3 = P1 - P3

3. 计算点 P(x, y, z) 到三角形三条边的叉积向量:

  • N1 = (P - P1) × V1
  • N2 = (P - P2) × V2
  • N3 = (P - P3) × V3

4. 如果三个叉积向量的方向全部相同,则点 `P` 在三角形内部。

5. 如果三个叉积向量的方向有任何不同,则点 `P` 在三角形外部。

矩阵

逆矩阵

一个矩阵是逆矩阵(inverse matrix)的前提是它是一个方阵(Square matrix)。

对于一个方阵\mathbf{M} ,它的逆矩阵\mathbf{M}^{-1} 有如下的性质:

\mathbf{MM^{-1}} = \mathbf{M^{-1}M} = \mathbf{I}

也不是所有的方阵都有逆矩阵,比如全部元素为0的方阵。

如果一个矩阵的行列式不为0,则它就是可逆的,有逆矩阵。

逆矩阵的几何意义就是,一个矩阵可以用来表示一个变换,而逆矩阵就可以还原这个变换。这很容易证明:

对于一个向量\mathbf{v} : \mathbf{M^{-1}Mv} = \mathbf{Iv} = \mathbf{v}

正交矩阵

如果一个方阵\mathbf{M} 与它的转置矩阵的乘积是一个单位矩阵,那么这个矩阵就是正交(orthogonal matrix),反过来也是成立。

\mathbf{MM^{T} = M^{T}M = I}

我们发现这个性质与逆矩阵很相似,所以我们能得出,如果一个矩阵正交,则:

\mathbf{M^{T} = M^{-1}}

这个式子非常有用,在三维变换中我们经常需要求解一个变换的逆矩阵来还原这个变换,而逆矩阵的计算量往往很大,而转置矩阵矩阵很容易求解。

如何知道一个方阵是否是正交矩阵?如果要判断\mathbf{MM^{T} = I} 从成立显然需要一定的计算量,可能和直接求解逆矩阵无异。因此我们更想不需要计算,而是仅仅从一个矩阵的构造过程来判断这个矩阵是否是正交矩阵,那么我们需要了解正交矩阵的几何意义。

3×3 的矩阵正交矩阵为例,根据正交矩阵的定义:

\begin{array}{l}\boldsymbol{M}^{\mathrm{T}} \boldsymbol{M}=\left[\begin{array}{ccc}- & \mathbf{c}_{1} & - \\- & \mathbf{c}_{2} & - \\- & \mathbf{c}_{3} & -\end{array}\right]\left[\begin{array}{lll}\mid & & \mid \\\mathbf{c}_{1} & \mathbf{c}_{2} & \mathbf{c}_{3} \\\mid & \mid & \mid\end{array}\right] \\=\left[\begin{array}{lll}\mathbf{c}_{1} \cdot \mathbf{c}_{1} & \mathbf{c}_{1} \cdot \mathbf{c}_{2} & \mathbf{c}_{1} \cdot \mathbf{c}_{3} \\\mathbf{c}_{2} \cdot \mathbf{c}_{1} & \mathbf{c}_{2} \cdot \mathbf{c}_{2} & \mathbf{c}_{2} \cdot \mathbf{c}_{3} \\\mathbf{c}_{3} \cdot \mathbf{c}_{1} & \mathbf{c}_{3} \cdot \mathbf{c}_{2} & \mathbf{c}_{3} \cdot \mathbf{c}_{3}\end{array}\right] \\= \left[\begin{array}{lll}1 & 0 & 0 \\0 & 1 & 0 \\0 & 0 & 1\end{array}\right]=\boldsymbol{I}\end{array}

这样我们就有9个等式

\begin{array}{lll}\mathrm{c}_{1} \cdot \mathrm{c}_{1}=1, & \mathrm{c}_{1} \cdot \mathrm{c}_{2}=0, & \mathrm{c}_{1} \cdot \mathrm{c}_{3}=0 \\\mathrm{c}_{2} \cdot \mathrm{c}_{1}=0, & c_{2} \cdot \mathrm{c}_{2}=1, & c_{2} \cdot \mathrm{c}_{3}=0 \\\mathrm{c}_{3} \cdot \mathrm{c}_{1}=0, & c_{3} \cdot \mathrm{c}_{2}=0, & c_{3} \cdot c_{3}=1\end{array}

那么:

  • 矩阵的每一行是一个单位矢量
  • 矩阵的每一行的矢量相互垂直

因为正交矩阵的性质,这个结果对于每一列也是适用的。如果矩阵满足这样的条件,那么它就是一个正交矩阵,而一组标准正交基满足这个条件。

正交基与标准正交基:

正交基是指在一个向量空间中,一组相互正交的基向量。这意味着基向量之间的点积为零。正交基向量不一定是单位向量,即它们的长度可以不是1。很常见的就是坐标轴。

标准正交基是一组不仅正交而且归一化的基向量。在标准正交基中,每个基向量不仅相互正交,而且都是单位向量(长度为1)。

比如(\sin\theta, -\cos\theta)(\cos\theta, \sin\theta) 就是一个二维空间中的标准正交基

也就是说如果我们使用标准正交基构造矩阵,例如\begin{bmatrix} \sin\theta & -\cos\theta \\ \cos\theta & \sin\theta \end{bmatrix} 那么这个矩阵一定就是正交矩阵。

行矩阵与列矩阵

当一个向量要用来和一个矩阵相乘时,我们就需要考虑将向量转换为行矩阵还是列矩阵。

要注意的是,因为矩阵相乘对两个矩阵的形式是有要求的,并且矩阵的计算顺序也会对结果有影响。

在图形学计算中,一般将矢量转化为列矩阵放在矩阵的右侧进行矩阵相乘。

变换

变换(transform)指的是把一些数据,如点、向量甚至是颜色通过某种方式转换的过程。

线性变换(linear transform)指的是只保留向量加和标量乘的变换。

f(x) + f(y) = f(x + y)

kf(x) = f(kx)

比如缩放变换是一个线性变换,例如放大两倍:f(x) = 2x ;旋转也是一种线性变换。

对于三维空间中的线性变换,仅用一个3×3 的矩阵就可以表示所有的线性变换。

线性变换还包括错切(shear)、镜像(mirroring或reflection)、正交投影

但线性变换并不能满足所有的变换,考虑平移变换f(x) = x + (n_1, n_2, n_3) ,线性变换只满足向量乘不满足向量加,因此三维空间中的变换,仅用一个3×3 的矩阵是不够的。

为了能够解决使用一个矩阵表示全部变换的问题,仿射变换(affine transform)出现了,它合并了线性变换和平移变换,先进行一次线性变换,再进行一次平移变换。

仿射变换可以用一个4×4 的矩阵来表示,扩展到了四维空间:齐次坐标空间(homogeneous space)下。

齐次坐标

变换矩阵扩展到4 ×4后,为了实现变换(矩阵乘法),向量也需要扩展到四维向量,也就是齐次坐标(homogeneous coordinate)。

对于一个点,扩展的方式是将其的w分量(x, y, z, w)设置为1,对于方向向量来说,则是将其w分量设置为0。这样的设计有很多原因与好处,最直接的是,对一个点进行齐次坐标的变换时,平移、旋转、缩放都会应用到这个点;而对于方向向量,平移不会应用。

我们将纯位移、纯旋转和纯缩放的变换矩阵叫做基础变换矩阵,而能够表示全部变换的齐次坐标下的4 ×4矩阵则可以这样分解:

\begin{bmatrix} \mathbf{M_{3×3}} &\mathbf{t_{3×1}} \ \mathbf{0_{1×3}} &1 \end{bmatrix}

M_{3×3}用于表示旋转和缩放,\mathbf{t_{3×1}}用于表示平移,\mathbf{0_{1×3}}是零矩阵

平移

对一个点进行平移变换:

\begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} =  \begin{bmatrix} x + t_x \\ y + t_y\\ z + t_z \\ 1 \end{bmatrix}

如果对一个方向向量进行平移操作则不会生效。

平移矩阵的逆矩阵,其实就是反向位移

\begin{bmatrix} 1 & 0 & 0 & -t_x \\ 0 & 1 & 0 & -t_y \\ 0 & 0 & 1 & -t_z \\ 0 & 0 & 0 & 1 \end{bmatrix}

很明显,平移矩阵不是正交矩阵。

缩放

对一个模型沿着x、y和z轴进行缩放:

\begin{bmatrix} k_x& 0 & 0 & 0 \\ 0 & k_y & 0 & 0 \\ 0 & 0 & k_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} k_xx \\ k_yy\\ k_zz \\ 1 \end{bmatrix}

缩放变换对于方向向量同样生效。

如果k_x = k_y = k_z,那么这个缩放就是一个统一缩放(uniform scale),否则就是非统一缩放(nonuniform scale)。从视觉上看,统一缩放就是按模型原有的比例去缩放模型,而非统一缩放会压缩或者拉伸模型,更重要的是,统一缩放不会改变一些信息,例如对法线进行变换时,如果使用非统一缩放,就会得到错误的结果。

如果缩放因子中有一个或者三个为负的分量,那么我们就获得了一个反射矩阵(reflection matrix)或者说镜像矩阵(mirror matrix);如果有两个为负的分量,那么这个矩阵会让物体旋转180度(中心对称)。

通常在检测到一个镜像变换的时候,都会进行一些特殊处理,例如一个顶点顺序为逆时针的三角形经过镜像变换之后变为顺时针,顶点顺序的改变会导致错误的光照效果和背面剔除。如果缩放矩阵的行列式的值为负数,说明这是一个反射矩阵。

缩放矩阵的逆矩阵:

\begin{bmatrix} \frac{1}{k_x} & 0 & 0 & 0 \\ 0 & \frac{1}{k_y} & 0 & 0 \\ 0 & 0 & \frac{1}{k_z} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

缩放矩阵(一般情况下)也不是正交矩阵。

注意,上面的矩阵用于沿着坐标轴缩放,如果要沿着任意方向进行缩放,则需要先进行一个变换改变朝向,使得缩放轴与坐标轴一致,之后进行缩放,最后使用一个逆变换将朝向变回来。

暂时无法在飞书文档外展示此内容

具体来说,如果给定一个方向\mathbf{f},首先将其分解为3个标准正交向量,然后构建旋转变换矩阵

\mathbf{F} = \begin{bmatrix} \mathbf{f^x} & \mathbf{f^y} & \mathbf{f^z} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

先变换会标准坐标系,也就是先乘以\mathbf{F^T},再进行缩放操作,之后再乘以\mathbf{F}变换会原来的坐标系

旋转

注意下面提到3个矩阵的旋转

将点绕x轴旋转\theta度:

\mathbf{R_x(\theta)} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta & 0 \\ 0 & \sin\theta & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

绕y轴旋转:

\mathbf{R_y(\theta)} = \begin{bmatrix} \cos\theta & 0 & \sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin\theta & 0 & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

绕z轴旋转:

\mathbf{R_z(\theta)} = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

旋转矩阵的逆矩阵,只需要旋转相反的角度就可以得到。

旋转矩阵是正交矩阵(注意,这里说的是非齐次坐标下的矩阵,也就是只有M_{3×3}的部分),而且多个旋转矩阵之间的串联同样是正交的。

如果我们想让物体以某个点为中心,绕三个轴旋转,那么我们可以先向物体平移,使得旋转点与原点重合,再进行旋转。

复合变换

我们可以将平移、缩放、旋转组合起来,而这个变换过程可以用下面的公式表示:

P' = M_{translation}M_{rotation}M_{scale}P

而根据矩阵乘法的结合律,TRS这三个矩阵可以提前计算合成一个矩阵P' = \mathbf{M}P,假如有几百万个点都需要应用同样的平移、缩放、旋转矩阵,用提前合成的一个矩阵要比分别使用三个矩阵计算要快得多。

之前提到了我们会将向量转换为列向量,所以上面公式的计算顺序实际上是从右向左;并且矩阵乘法时,矩阵的计算顺序会影响计算结果,也就是我们需要确定好变换的顺序,在绝大多数情况下,我们约定的变换顺序是先缩放,再旋转,最后平移。

除了需要注意不同类型的变换顺序外,我们有时还需要小心旋转的变换顺序。当给出(\theta_x, \theta_y, \theta_z)的旋转角度时,我们需要定义旋转顺序。在Unity中,这个旋转顺序是zxy,这在旋转相关的API文档中都有说明,

但得到的分解的旋转变换矩阵是:

M_{rotateZ}M_{rotateX}M_{rotateY}

这个矩阵与上面说的计算顺序从右向左冲突了,这是因为有两种不同的旋转方式(即两种不同的坐标系选择):

第一种方式:

对于3次旋转,每次旋转都相对于原始固定坐标系E进行

第二种方式:

每次旋转都相对于上一次旋转后的新坐标系进行。

简单举例来说,如果在Unity中调用transform.Rotate(30, 40, -50),使用的就是第一种旋转方式,以全局坐标系的顺序进行旋转的,即先旋转 Z 轴,再旋转 X 轴,最后旋转 Y 轴,所有的旋转都是基于物体初始的坐标系。

而如果分开调用 transform.Rotate(new Vector3(0, 90, 0));transform.Rotate(new Vector3(30, 0, 0));transform.Rotate(new Vector3(0, 0, -40)); 的情况,每一次调用都会改变物体的局部坐标系,正是因为每次旋转都改变旋转坐标系,所以倒序得到的就和一次调动的结果相同,这就是分解后旋转矩阵是倒序的原因

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 左手系与右手系
  • 向量
  • 矩阵
    • 逆矩阵
      • 正交矩阵
        • 行矩阵与列矩阵
        • 变换
          • 齐次坐标
            • 平移
              • 缩放
                • 旋转
                  • 复合变换
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档