欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > UnityShader——基础篇之学习Shader所需的数学基础——上

UnityShader——基础篇之学习Shader所需的数学基础——上

2024/10/23 4:32:21 来源:https://blog.csdn.net/Aubyn11/article/details/142864566  浏览:    关键词:UnityShader——基础篇之学习Shader所需的数学基础——上

学习Shader所需的数学基础

  • 笛卡尔坐标系
    • 二维笛卡尔坐标系
    • 三维笛卡尔坐标系
    • 左手坐标系和右手坐标系
    • Unity使用的坐标系
  • 点和矢量
    • 点和适量的区别
    • 矢量运算
      • 1. 矢量和标量乘法/除法
      • 2.矢量的加法和减法
      • 3.矢量的模
      • 4.单位矢量
      • 5.矢量的点积
      • 6.矢量的叉积
  • 矩阵
    • 矩阵的定义
    • 和矢量联系起来
    • 矩阵运算
      • 1.矩阵和标量的乘法
      • 2.矩阵和矩阵的乘法
    • 特殊的矩阵
      • 1.方块矩阵
      • 2.单位矩阵
      • 3.转置矩阵
      • 4.逆矩阵
      • 5.正交矩阵
  • 矩阵的几何意义:变换
    • 什么是变换
    • 分解基础变换矩阵
    • 平移矩阵
    • 缩放矩阵
    • 旋转矩阵
    • 复合变换
  • 坐标空间
    • 坐标空间的转换
    • 顶点的坐标空间变换过程
    • 模型空间
    • 世界空间
    • 观察空间

笛卡尔坐标系

二维笛卡尔坐标系

  一个二维的笛卡尔坐标系包含了两个部分的信息:

  • 一个特殊的位置,即原点,它是整个坐标系的中心
  • 两条过原点的互相垂直的矢量,即 x x x轴和 y y y轴。这些坐标轴也被称为是该坐标系的基矢量

  坐标系中的 x x x轴和 y y y轴不一定是水平和垂直方向的

  有了这个坐标系就可以精确的定位一个点的位置

三维笛卡尔坐标系

  三维笛卡尔坐标系相较于二维来说要复杂许多,但这并不意味着很难学会他,对人类来说,生活的世界就是三维的,因此对于理解更低维度的空间(一维和二维)是比较容易地。而对于同等维度的一些概念;理解起来难度就大一些;对于更高维度的空间(如四维空间),理解难度就更大了

  在三维笛卡尔坐标系中需要定义3个坐标轴和一个原点,如图
在这里插入图片描述
  这3个坐标轴也被称为是该坐标系的基矢量,通常情况下,这3个坐标轴之间是相互垂直的,且长度为1,这样的基矢量被称为标准正交基,但这并不是必须的。例如,在一些坐标系中坐标轴之间互相垂直但长度不为1,这样的基矢量被称为正交基。如非特殊说明,默认情况下使用的坐标轴指的都是标准正交基

  正交可以理解成相互垂直的意思

  和二维笛卡尔坐标系类似,三位笛卡尔坐标系中的坐标轴方向也不是固定的,即不一定是像上图中那样的指向。但这种不同导致了两种不同种类的坐标系:左手坐标系右手坐标系

左手坐标系和右手坐标系

  为什么在三维笛卡尔坐标系中要区分左手坐标系和右手坐标系,而二维中就没有这些事情?这是因为,在二维笛卡尔坐标系中, x x x轴和 y y y轴的只想虽然可能不同,但总可以通过一些旋转操作来使它们的坐标轴指向相同

  对于三维笛卡尔坐标系,靠旋转并不能使两个不同朝向的坐标系重合。也就是说,三维笛卡尔坐标系并不都是等价的。因此,就出现了两种不同的三维坐标系:左手坐标系和右手坐标系。如果两个坐标系具有相同的旋向性,那么就可以通过旋转的方法来让它们的坐标系重合。但是,如果他们具有不同的旋向性,那么就无法达到重合的目的

  为什么叫左手坐标系和右手坐标系?这是因为,可以利用双手来判断一个坐标轴的旋向性,举起左手,用食指和大拇指摆出一个”L“的收拾,并且让食指指向上,大拇指指向右。现在伸出中指,不出意外的话它应该指向前方,这就是左手坐标系;同样的,举起右手,食指指向上,中指指向前方,拇指指向左侧,这就是右手坐标系,如图

在这里插入图片描述
  除了坐标系朝向不同之外,左手坐标系和右手坐标系对于正向旋转的定义也不同,即在出高中物理中学到的左手法则右手法则

  左右手坐标系是可以进行相互转换的。最简单的方法就是把其中一个轴反转,并保持其他两个轴不变

  对于开发者来说,使用左手坐标系还是右手坐标系都是可以的,它们之间并没有优劣之分。无论使用哪种坐标系,绝大多数情况下并不会影响底层的数学运算,而只是在映射到视觉上时会有差别。这是因为一个点或者旋转在空间内来说时绝对的

Unity使用的坐标系

  Unity使用的时左手坐标系,这可以从Scene试图的坐标轴显示可以看出来,如图

在这里插入图片描述
  这意味着,在模型空间中,一个物体的右侧、上侧和前侧分别对应了 x x x轴, y y y轴, z z z轴的正方向

  但对于观察空间来说,Unity使用的是右手坐标系。观察空间通俗来讲就是以摄像机为原点的坐标系。在这个坐标系中,摄像机的前向时 z z z轴的负方向,这与在模型空间和世界空间中的定义相反。也就是说, z z z轴坐标的减少意味着场景深度的增加,如图
在这里插入图片描述

在Unity中,观察空间使用的是右手坐标系,摄像机的前向是 z z z轴的负方向, z z z轴越小,物体的深度越大,离摄像机越远

点和矢量

   n n n维空间中的一个位置,他没有大小、宽度这类概念。在笛卡尔坐标系中,可以使用2个或3个实数来表示一个点的坐标

  矢量的定义则复杂一些。矢量存在的意义更多是为了和标量区分开来。通常来讲,矢量是指 n n n维空间中一种包含了方向的有向线段

  具体来讲

  • 矢量的模指的是这个矢量的长度。一个矢量的长度可以是任意的非负数
  • 矢量的方向则描述了这个矢量在空间中的指向。

  矢量的表示方法和点类似。可以使用 v = ( x , y ) v = (x,y) v=(x,y)来表示二维矢量,用 v = ( x , y , z ) v=(x,y,z) v=(x,y,z)来表示三维矢量,用 v = ( x , y , z , w ) v=(x,y,z,w) v=(x,y,z,w)来表示四维矢量

  一个适量通常由一个箭头表示,有时会讲到一个矢量的。矢量的头指的是它的箭头所在的端点处,而尾指的是另一个端点处,如图
在这里插入图片描述
  通常,矢量被用于表示相对于某个点的偏移,也就是说它是一个相对量。只要矢量的模和方向保持不变,无论在哪里都是一个矢量

点和适量的区别

  上节提到矢量通常用于描述偏移量,因此,它们可以用于描述相对位置,即相对于另一个点的位置,此时矢量的尾是一个位置,那么矢量的头就可以表示另一个位置了。而一个点可以用于指定空间中的一个位置(即相对于原点的位置)。如果把矢量的尾固定在坐标系原点,那么这个矢量的表示就和点的表示重合了,如图
在这里插入图片描述

矢量运算

1. 矢量和标量乘法/除法

  虽然不能把矢量和标量进行相加/相减的运算,但可以对他们进行乘法运算,结果会得到一个不同长度且可能方向相反的新的矢量

  公式非常简单,只需要把矢量的每个分量和标量相乘即可:

k v = ( k v x , k v y , k v z ) kv = (kv_x,kv_y,kv_z) kv=(kvx,kvy,kvz)

  类似的,一个适量也可以被一个非零的标量除。这等同于和这个标量的倒数相乘:

v k = ( x , y , z ) k = 1 k ( x , y , z ) = ( x k , y k , z k ) , k ≠ 0 \frac{\mathbf{v}}{k} = \frac{(x,y,z)}{k} = \frac{1}{k}(x,y,z) = \left(\frac{x}{k},\frac{y}{k},\frac{z}{k}\right),k \neq 0 kv=k(x,y,z)=k1(x,y,z)=(kx,ky,kz),k=0

  注意,对于惩罚来说,矢量和标量的位置可以互换,但对于出发,只能是矢量被标量除,而不能被矢量除,这是没有意义的

  从几何意义上看,把一个 v \mathbf{v} v和一个标量 k k k相乘,意味着对矢量 v \mathbf{v} v进行一个大小为 ∣ k ∣ |k| k的缩放,如下图例
在这里插入图片描述

2.矢量的加法和减法

  可以对两个矢量进行相加或相减,其结果是一个相同维度的新矢量

  只需要把两个矢量的对应分量进行相加或相减即可。公式如下
a + b = ( a x + b x , a y + b y , a z + b z ) a − b = ( a x − b x , a y − b y , a z − b z ) \begin{aligned}\mathbf{a} + \mathbf{b} = (a_x+b_x,a_y+b_y,a_z+b_z)\\ \mathbf{a} - \mathbf{b} = (a_x-b_x,a_y-b_y,a_z-b_z)\end{aligned} a+b=(ax+bx,ay+by,az+bz)ab=(axbx,ayby,azbz)

  需要注意的是,一个矢量不可以和一个标量相加或相减,或者是和不同维度的矢量进行运算

  从几何意义上来看,对于减法,可以把矢量 a \mathbf{a} a的头连接到矢量 b \mathbf{b} b的尾,然后画一条从 a \mathbf{a} a的尾到 b \mathbf{b} b的头的矢量,来得到 a \mathbf{a} a b \mathbf{b} b相加后的矢量。也就是说,如果从一个起点开始进行了一个位置偏移 a \mathbf{a} a,然后又进行了一个位置偏移 b \mathbf{b} b,那么就等同于进行了一个 a + b \mathbf{a+b} a+b的位置偏移。这被称为矢量加法的三角形定则。矢量的减法是类似的,如图
在这里插入图片描述
  需要时刻谨记,在图形学中矢量通常用于描述位置偏移(简称位移)。因此,可以利用适量的加法和减法来计算一点相对于另一点的位移

  假设,空间内有两点 a a a b b b,如果想要计算点 b b b相对于点 a a a的位移,就可以通过把 b b b a a a相减得到,如图
在这里插入图片描述

3.矢量的模

  矢量的模是一个标量,可以理解为是矢量在空间中的长度。它的表示符号通常是在矢量两旁分别加上一条垂直线(有的文献中会使用两条垂直线)。三维矢量的模的计算公式如下

∣ v ∣ = v x 2 + v y 2 + v z 2 |\mathbf{v}| = \sqrt{v_x^2+v_y^2+v_z^2} v=vx2+vy2+vz2

  其他维度的矢量的模计算类似,都是对每个分量的平方相加后再开根号得到

  可以从几何意义来理解上述公式。对于二维矢量来说,可以对任意矢量构建一个三角形,如图
在这里插入图片描述
  由图可以看出,对于二维矢量,其实就是使用了勾股定理,矢量的两个分量的绝对值对应了三角形两个直角边的长度,而斜边的长度,而斜边的长度就是矢量的模

4.单位矢量

  单位矢量指的是那些模为 1 的矢量。单位矢量也被称为归一化的矢量。对任何给定的非零矢量,把它转换成单位矢量的过程就被称为归一化

  给定任意非零矢量 v \mathbf{v} v,可以计算和 v \mathbf{v} v方向相同的单位矢量。为了对矢量进行归一化,可以用矢量初一该矢量的模来得到。公式如下:

v ^ = v ∣ v ∣ , v 是任意非零矢量 \mathbf{\hat v} = \mathbf{\frac{v}{|v|}},\mathbf{v}是任意非零矢量 v^=∣v∣vv是任意非零矢量

  零矢量(即矢量的每个分量值都为0)是不可以被归一化的。这是因为做除法运算时分母不为0.

  从几何意义上看,对二维空间来说,可以画一个单位圆,那么单位矢量就可以是从圆心出发、到圆边界的矢量,在三维空间中,单位矢量就是从一个单位球的求新出发、到达球面的矢量。如下图给出了二维空间内的一些单位矢量。

在这里插入图片描述

5.矢量的点积

  适量之间也可以进行乘法,但是和标量之间的乘法有很大不同。矢量的乘法有两种最常用的种类:点积(也被称为内积)叉积(也被称为外积)

  点积的名称来源于这个运算的符号: a ⋅ b \mathbf{a} \cdot \mathbf{b} ab。中间的圆点符号是不可以省略的。点积的公式有两种形式,先看第一种。两个三维矢量的点积是把两个矢量对应分量相乘后再取和,最后的结果是一个标量

  公式一:

a ⋅ b = ( a x , a y , a z ) ⋅ ( b x , b y , b z ) = a x b x + a y b y + a z b z \mathbf{a} \cdot \mathbf{b} = (a_x,a_y,a_z) \cdot (b_x,b_y,b_z) = a_xb_x+a_yb_y+a_zb_z ab=(ax,ay,az)(bx,by,bz)=axbx+ayby+azbz

  矢量的点积满足交换律,即 a ⋅ b = b ⋅ a \mathbf{a \cdot b = b \cdot a} ab=ba

  点积的几何意义很重要,因为点积几乎应用到了图形学的各个方面。其中一个几何意义就是投影

  假设,有一个单位矢量 a ^ \mathbf{\hat {a}} a^和另一个长度不限的矢量 b \mathbf{b} b。现在,希望得到 b \mathbf{b} b在平行于 a ^ \mathbf{\hat{a}} a^的一条直线上的投影。那么,就可以使用点积 a ^ ⋅ b \mathbf{\hat{a} \cdot b} a^b来得到 b \mathbf{b} b a ^ \mathbf{\hat{a}} a^方向上的有符号的阴影

  投影是什么意思?一个通俗的解释为:现在有一个光源,它发出的光线是垂直于 a ^ \mathbf{\hat{a}} a^方向的,那么 b \mathbf{b} b a ^ \mathbf{\hat{a}} a^方向上的投影就是 b \mathbf{b} b a ^ \mathbf{\hat{a}} a^方向上的影子,如图所示

在这里插入图片描述
  需要注意的是,投影的值可能是附属。投影结果的正负号与 a ^ \mathbf{\hat{a}} a^ b \mathbf{b} b方向有关:当它们的方向相反(夹角大于 9 0 ∘ 90^\circ 90)时,结果小于0;当它们的方向相互垂直(夹角为 9 0 ∘ 90^\circ 90)时,结果等于0;当它们的方向相同(夹角小于 9 0 ∘ 90^\circ 90)时,结果大于0,如图所示

在这里插入图片描述
  也就是说,点积的符号可以让我们知道两个适量的方向关系

  那么,如果 a ^ \mathbf{\hat{a}} a^不是一个单位矢量会如何?任何两个矢量的点积 a ⋅ b \mathbf{a \cdot b} ab等同于 b \mathbf{b} b a \mathbf{a} a方向上的投影值,再乘以 a \mathbf{a} a的长度

  点积有一些很重要的性质:

  性质一:点积可结合标量乘法

  上面的“结合”是说,点积的操作数之一可以是另一个运算的结果,即矢量和标量相乘的结果。公式如下:

( k a ) ⋅ b = a ⋅ ( k b ) = k ( a ⋅ b ) (k\mathbf{a})\cdot \mathbf{b} = \mathbf{a}\cdot(k\mathbf{b}) = k(\mathbf{a \cdot b}) (ka)b=a(kb)=k(ab)

  也就是说,对点积中其中一个矢量进行缩放的结果,相当于对最后的点积结果进行缩放

  性质二:点积可结合矢量加法和减法,和性质一类似

  这里的“结合”指的是,点积的操作数可以是矢量相加或相减后的结果。用公式表达如下:

a ⋅ ( b + c ) = a ⋅ b + a ⋅ c \mathbf{a\cdot(b+c)}=\mathbf{a\cdot b + a\cdot c} a(b+c)=ab+ac

  把上面的 c \mathbf{c} c换成 − c \mathbf{-c} c就可以得到减法的版本

  性质三:一个适量和本身进行点积的结果,是该矢量的模的平方

  这点可以很容易从公式验证中得到:

v ⋅ v = v x v x + v y v y + v z v z = ∣ v ∣ 2 \mathbf{v\cdot v} = v_xv_x + v_yv_y + v_zv_z = |\mathbf{v}|^2 vv=vxvx+vyvy+vzvz=v2

  这意味着,可以直接利用点击来求矢量的模,而不需要使用模的计算公式。淡然,需要对点积结果进行开平方的操作来得到真正的模。但很多情况下,只是想要比较两个向量的长度大小,因此可以直接使用点积的结果。毕竟,开平方的运算需要消耗一定的性能

  点积的另一种表示方法。这种方法时从三角代数的角度出发的,这种表示方法更加具有几何意义,因此它可以明确的强调出两个矢量之间的角度

  公式二:

a ⋅ b = ∣ a ∣ ∣ b ∣ cos ⁡ θ \mathbf{a \cdot b}=|\mathbf{a}||\mathbf{b}|\cos \theta ab=a∣∣bcosθ

  两个矢量的点积可以表示为两个矢量的模相乘,再乘以他们之间夹角的余弦值,从这个公式也可以看出,为什么计算投影时两个矢量的方向不同会得到不同符号的投影值:当夹角小于 9 0 ∘ 90^\circ 90时, cos ⁡ θ > 0 \cos\theta>0 cosθ>0;当夹角等于 9 0 ∘ 90^\circ 90时, cos ⁡ θ = 0 \cos\theta=0 cosθ=0;当夹角大于 9 0 ∘ 90^\circ 90时, cos ⁡ θ < 0 \cos\theta<0 cosθ<0

  利用这个公式还可以求得两个向量之间的夹角(在 0 ∘ ∼ 18 0 ∘ 0^\circ \sim 180^\circ 0180):

θ = a r c o s ( a ^ ⋅ b ^ ) , 假设 a ^ 和 b ^ 时单位矢量 \theta=arcos(\mathbf{\hat{a}\cdot\hat{b}}),假设\mathbf{\hat{a}}和\mathbf{\hat{b}}时单位矢量 θ=arcos(a^b^),假设a^b^时单位矢量

  其中,arcos是反余弦函数

6.矢量的叉积

  另一个重要的矢量运算就是叉积,也被称为外积。与点积不同的是,矢量叉积的结果仍是一个矢量,而非标量

  和点积类似,叉积的名称来源于它的符号: a × b \mathbf{a \times b} a×b。同样,这个叉号也是不可省略的。两个矢量的叉积可以用如下公式计算:

a × b = ( a x , a y , a z ) × ( b x , b y , b z ) = ( a y b z − a z b y , a z b x − a x b z , a x b y − a y b x ) \mathbf{a \times b} = (a_x,a_y,a_z)\times(b_x,b_y,b_z)=(a_yb_z-a_zb_y,a_zb_x-a_xb_z,a_xb_y-a_yb_x) a×b=(ax,ay,az)×(bx,by,bz)=(aybzazby,azbxaxbz,axbyaybx)

  上面的公式看起来很复杂,但其实是有一定规律的,如图所示:

在这里插入图片描述
  需要注意的是,叉积不满足交换律,即 a × b ≠ b × a \mathbf{a \times b \neq b \times a} a×b=b×a。实际上,叉积是满足反交换律的,即 a × b = − ( b × a ) \mathbf{a\times b = -(b \times a)} a×b=(b×a)。而且叉积也不满足结合律,即 ( a × b ) × c ≠ a × ( b × c ) (\mathbf{a \times b})\times c \neq \mathbf{a\times(b\times c)} (a×b)×c=a×(b×c)

  从叉积的几何意义出发,可以更加深入的了解它的用处。对两个矢量进行叉积的结果会得到一个同时垂直于这两个矢量的新矢量

  叉积有什么作用?最常见的一个应用就是计算垂直于一个平面、三角形的矢量。另外,还可以用于判断三角面片的朝向

矩阵

矩阵的定义

  矩阵是由 m × n m \times n m×n个标量组成的长方形数组。既然是网状结构,就意味着矩阵由行列之分,据此,可以给出矩阵的一般表达式,如下:

[ m 11 m 12 m 13 m 21 m 22 m 23 m 31 m 32 m 33 ] \begin{bmatrix} m_{11}&m_{12}&m_{13}\\ m_{21}&m_{22}&m_{23}\\ m_{31}&m_{32}&m_{33} \end{bmatrix} m11m21m31m12m22m32m13m23m33

   m i j \mathbf{m_{ij}} mij表明了这个元素在矩阵 M \mathbf{M} M的第 i i i行、第 j j j

和矢量联系起来

  矢量可以看成是 n × 1 n\times1 n×1列矩阵 1 × n 1\times n 1×n行矩阵,其中 n n n对应了矢量的维度

  为什么要把矢量和矩阵联系在一起?这是为了可以让矢量像一个矩阵一样一起参与矩阵运算。这在空间变换中非常有用

矩阵运算

1.矩阵和标量的乘法

  和矢量类似,矩阵也可以和标量相乘,它的结果仍然是一个相同维度的矩阵。它们之间的乘法非常简单,就是矩阵的每个元素和该标量相乘。以 3 × 3 3\times 3 3×3的矩阵为例,其公式如下:

k M = M k = k [ m 11 m 12 m 13 m 21 m 22 m 23 m 31 m 32 m 33 ] = [ k m 11 k m 12 k m 13 k m 21 k m 22 k m 23 k m 31 k m 32 k m 33 ] k\mathbf{M} = \mathbf{Mk} = k\begin{bmatrix} m_{11}&m_{12}&m_{13}\\ m_{21}&m_{22}&m_{23}\\ m_{31}&m_{32}&m_{33} \end{bmatrix} = \begin{bmatrix} km_{11}&km_{12}&km_{13}\\ km_{21}&km_{22}&km_{23}\\ km_{31}&km_{32}&km_{33} \end{bmatrix} kM=Mk=k m11m21m31m12m22m32m13m23m33 = km11km21km31km12km22km32km13km23km33

2.矩阵和矩阵的乘法

  两个矩阵的乘法也很简单,它们的结果会是一个新的矩阵,并且这个矩阵的维度和两个原矩阵的维度都有关系

  一个 r × n r\times n r×n的矩阵 A \mathbf{A} A和一个 n × c n \times c n×c的矩阵 B \mathbf{B} B相乘,他们的结果 A B \mathbf{AB} AB将会是一个 r × c r\times c r×c大小的矩阵。

  如果两个矩阵的行列不满足上面的规定则这两个矩阵就不能相乘,因为它们之间的乘法是没有被定义的

  在Shader的计算中,更多的使用时 4 × 4 4\times 4 4×4矩阵来运算的

  矩阵乘法满足一些性质:

  性质一:矩阵乘法并不满足交换律,也就是说,通常情况下

A B ≠ B A \mathbf{AB\neq BA} AB=BA

  性质二:矩阵乘法满足结合律,也就是说

( A B ) C = A ( B C ) \mathbf{(AB)C=A(BC)} (AB)C=A(BC)

  矩阵乘法的结合律可以拓展到更多矩阵的相乘。例如:

A B C D E = ( ( A ( B C ) D ) E = ( A B ) ( C D ) E \mathbf{ABCDE=((A(BC)D)E=(AB)(CD)E} ABCDE=((A(BC)D)E=(AB)(CD)E

特殊的矩阵

1.方块矩阵

  方块矩阵,简称方阵,是指那些行和列数目相等的矩阵。在三维渲染里,最常使用的就是 3 × 3 3\times 3 3×3 4 × 4 4\times 4 4×4的方阵

  方阵之所以单独单出来,是因为矩阵的一些运算和性质是只有方阵才具有的。例如对角矩阵。方阵的对角元素指的是行号和列号相等的元素,例如 m 11 、 m 22 、 m 33 m_{11}、m_{22}、m_{33} m11m22m33等。如果一个矩阵除了对角元素外的所有元素都为0,那么这个矩阵就叫做对角矩阵,例如:

[ 3 0 0 0 0 − 2 0 0 0 0 1 0 0 0 0 7 ] \begin{bmatrix} 3&0&0&0\\ 0&-2&0&0\\ 0&0&1&0\\ 0&0&0&7 \end{bmatrix} 3000020000100007

2.单位矩阵

  一个特殊的对角矩阵是单位矩阵,用 I n \mathbf{I_{n}} In来表示。一个 3 × 3 3\times 3 3×3的单位矩阵如下:

I 3 = [ 1 0 0 0 1 0 0 0 1 ] \mathbf{I_3} = \begin{bmatrix} 1&0&0\\ 0&1&0\\ 0&0&1 \end{bmatrix} I3= 100010001

  这种矩阵单独起一个名字的原因是因为和它相乘的结果都还是原来的矩阵,也就是说:

M I = I M = M \mathbf{MI=IM=M} MI=IM=M

  这就和标量中的1一样

3.转置矩阵

  转置矩阵实际是对原矩阵的一种运算,即转置运算。给定一个 r × c r\times c r×c的矩阵 M \mathbf{M} M,它的转置可以表示成 M T \mathbf{M}^T MT,这是一个 c × r c\times r c×r的矩阵。转置矩阵的计算非常简单,只需要把原矩阵反转一下即可。也就是说,原矩阵的第 i i i行变成了第 i i i列,而第 j j j列变成了第 j j j行。数学公式是:

M i j T = M j i \mathbf{M}_{ij}^T=\mathbf{M}_{ji} MijT=Mji

  对于行矩阵和列矩阵来说,可以使用转置操作来转换行列矩阵:

[ x y z ] T = [ x y z ] [ x y z ] T = [ x y z ] \begin{bmatrix} x&y&z \end{bmatrix}^T = \begin{bmatrix} x\\ y\\ z \end{bmatrix}\\ \begin{bmatrix} x\\ y\\ z \end{bmatrix}^T = \begin{bmatrix} x&y&z \end{bmatrix} [xyz]T= xyz xyz T=[xyz]

  转置矩阵也有一些常用的性质

  性质一:矩阵的转置等于原矩阵

  很容易理解,把一个矩阵翻转一下后再翻转一下,等于没有对矩阵做任何操作。即:

( M T ) T = M (\mathbf{M}^T)^T = \mathbf{M} (MT)T=M

  性质二:矩阵串接的转置,等于反向串接各个矩阵的转置,用公式表示就是:

( A B ) T = B T A T (\mathbf{AB})^T=\mathbf{B}^T\mathbf{A}^T (AB)T=BTAT

  该性质同样可以拓展到更多矩阵相乘的情况

4.逆矩阵

  不是所有的矩阵都有逆矩阵,第一个前提就是,该矩阵必须是一个方阵

  给定一个方阵 M \mathbf{M} M,它的逆矩阵用 M − 1 \mathbf{M}^{-1} M1来表示。逆矩阵最重要的性质就是,如果把 M \mathbf{M} M M − 1 \mathbf{M}^{-1} M1相乘,那么他们的结果将会是一个单位矩阵。也就是说:

M M − 1 = M − 1 M = I \mathbf{MM}^{-1} = \mathbf{M}^{-1}\mathbf{M} = \mathbf{I} MM1=M1M=I

  如果一个矩阵有对应的逆矩阵,那么这个矩阵是可逆的或者说是非奇异的;相反的,如果一个矩阵没有对应的逆矩阵,那么就是不可逆的或者说是奇异的

&emps; 如果一个矩阵的行列式不为0,那么它就是可逆的。在写Shader的过程中,这些矩阵通常可以通过调用第三方库(如C++数学库Eigen)来直接求得,不需要开发者手动计算。在Unity中,重要变换矩阵的逆矩阵Unity也提供了相应的变量以供使用。

  逆矩阵有很多非常重要的性质

  性质一:逆矩阵的逆矩阵是原矩阵本身。假设矩阵 M \mathbf{M} M是可逆的,那么

( M − 1 ) − 1 = M (\mathbf{M}^{-1})^{-1} = \mathbf{M} (M1)1=M

&emps; 性质二:单位矩阵的逆矩阵是他本身,即:

I − 1 = I \mathbf{I}^{-1} = \mathbf{I} I1=I

  性质三:转置矩阵的逆矩阵是逆矩阵的转置,即:

( M T ) − 1 = ( M − 1 ) T (\mathbf{M}^{T})^{-1} = (\mathbf{M}^{-1})^T (MT)1=(M1)T

  性质四:矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵,即:

( A B ) − 1 = B − 1 A − 1 (\mathbf{AB})^{-1} = \mathbf{B}^{-1}\mathbf{A}^{-1} (AB)1=B1A1

  这个性质可以扩展到更多矩阵地连乘

  逆矩阵是具有几何意义的,因为一个矩阵可以表示一个变换,而逆矩阵允许还原这个变换,或者说是计算这个变换的反向变换。因此,如果使用变化矩阵 M \mathbf{M} M对矢量 v \mathbf{v} v进行了一次变换,然后再使用它的逆矩阵 M − 1 \mathbf{M}^{-1} M1进行另一次变换,那么会得到原来的矢量。

5.正交矩阵

  另一个特殊的方阵是正交矩阵。正交是矩阵的一种属性。如果一个方阵 M \mathbf{M} M和它的转置矩阵的乘积是单位矩阵的话,就说这个矩阵是正交的。反过来也是成立的。也就是说,矩阵 M \mathbf{M} M是正交的等价于:

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

  上式和逆矩阵的公式很像。把这两个公式结合起来,就可以得到一个重要的性质,即如果一个矩阵是正交的,那么它的转置矩阵和逆矩阵是一样的。也就是说,矩阵 M \mathbf{M} M是正交的等价于:

M T = M − 1 \mathbf{M}^T=\mathbf{M}^{-1} MT=M1

  这个式子非常有用,因为在三维变换中经常会需要使用逆矩阵来求解反向的变换。而逆矩阵的求解往往计算量很大,但转置矩阵却非常容易求解。为了提前判断一个矩阵是否正交,需要聊欧杰正交矩阵的几何意义

  根据正交矩阵的定义,有:

M T M = [ − c 1 − − c 2 − − c 3 − ] [ ∣ ∣ ∣ c 1 c 2 c 3 ∣ ∣ ∣ ] = [ c 1 ⋅ c 1 c 1 ⋅ c 2 c 1 ⋅ c 3 c 2 ⋅ c 1 c 2 ⋅ c 2 c 2 ⋅ c 3 c 3 ⋅ c 1 c 3 ⋅ c 2 c 3 ⋅ c 3 ] = [ 1 0 0 0 1 0 0 0 1 ] = I \mathbf{M}^T\mathbf{M} = \begin{bmatrix} -&c_1&-\\ -&c_2&-\\ -&c_3&- \end{bmatrix}\begin{bmatrix} |&|&|\\ c_1&c_2&c_3\\ |&|&| \end{bmatrix}=\begin{bmatrix} c_1\cdot c_1&c_1\cdot c_2&c_1\cdot c_3\\ c_2\cdot c_1&c_2\cdot c_2&c_2\cdot c_3\\ c_3\cdot c_1&c_3\cdot c_2&c_3\cdot c_3 \end{bmatrix} = \begin{bmatrix} 1&0&0\\ 0&1&0\\ 0&0&1 \end{bmatrix}=\mathbf{I} MTM= c1c2c3 c1c2c3 = c1c1c2c1c3c1c1c2c2c2c3c2c1c3c2c3c3c3 = 100010001 =I

  这样,就有了9个等式:

c 1 ⋅ c 1 = 1 , c 1 ⋅ c 2 = 0 , c 1 ⋅ c 3 = 0 c 2 ⋅ c 1 = 0 , c 2 ⋅ c 2 = 1 , c 2 ⋅ c 3 = 0 c 3 ⋅ c 1 = 0 , c 3 ⋅ c 2 = 0 , c 3 ⋅ c 3 = 1 c_1\cdot c_1=1,c_1\cdot c_2=0,c_1\cdot c_3=0\\ c_2\cdot c_1=0,c_2\cdot c_2=1,c_2\cdot c_3=0\\ c_3\cdot c_1=0,c_3\cdot c_2=0,c_3\cdot c_3=1 c1c1=1,c1c2=0,c1c3=0c2c1=0,c2c2=1,c2c3=0c3c1=0,c3c2=0,c3c3=1

  可以得到以下结论:

  • 矩阵的每一行,即 c 1 、 c 2 c_1、c_2 c1c2 c 3 c_3 c3是单位矢量,因为只有这样他们与自己的点积才能是1;
  • 矩阵的每一行,即 c 1 、 c 2 c_1、c_2 c1c2 c 3 c_3 c3之间相互垂直,因为只有这样它们之间的点积才能是0.
  • 上述两条结论对矩阵的每一列同样使用,因为如果 M \mathbf{M} M是正交矩阵的话, M T \mathbf{M}^T MT也会是正交矩阵

  也就是说,如果一个矩阵满足上面的条件,那么它就是一个正交矩阵。如果这些基矢量是一组标准正交基的话,那么就可以直接使用转置矩阵来求得该变换的逆变换

矩阵的几何意义:变换

什么是变换

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

  线性变换,线性变换指的是那些可以保留矢量加和标量乘的变换。用数学公式来表示这两个条件就是:

f ( x ) + f ( y ) = f ( x + y ) k f ( x ) = f ( k x ) \mathbf{f(x) + f(y)=f(x+y)}\\ k\mathbf{f(x)=f}(k\mathbf{x}) f(x)+f(y)=f(x+y)kf(x)=f(kx)

  缩放是一种线性变换,例如 f ( x ) = 2 x \mathbf{f(x)=2x} f(x)=2x,同样,旋转也是线性变换。对于线性变换来说,如果要对一个三维的矢量进行变换,那么仅仅使用 3 × 3 3\times 3 3×3的矩阵就可以表示所有的线性变换。

  线性变换除了包括旋转和缩放外,还包括错切、镜像、正交投影等。

  但是,仅有线性变换是不够的。考虑平移变换,例如 f ( x ) = x + ( 1 , 2 , 3 ) \mathbf{f}(x)=x+(1,2,3) f(x)=x+(1,2,3)。这个变换就不是一个线性变换,它既不满足标量乘法,也不满足矢量加法。

  因此,不能用一个 3 × 3 3\times 3 3×3的矩阵来表示一个变换。这是不希望看到的,毕竟平移变换是非常常见的一种变换

  这样,就有了仿射变换。仿射变换就是合并线性变换和平移变换的变换类型。仿射变换可以使用一个 4 × 4 4\times 4 4×4的矩阵来表示,为此,需要把矢量拓展到四维空间下,这就是齐次坐标空间

  如下给出了图形学中常见变换矩阵的名称和它们的特性。

常见的变换种类和它们的特性N表示不满足该特性,Y表示满足该特性
变换名称是线性变换吗是仿射变换吗是可逆矩阵吗是正交矩阵吗
平移矩阵NYYN
绕坐标轴旋转的旋转矩阵YYYY
绕任意轴旋转的旋转矩阵YYYY
按坐标轴缩放的缩放矩阵YYYN
错切矩阵YYYN
镜像矩阵YYYY
正交投影矩阵YYNN
透视投影矩阵NNNN
## 齐次坐标   由于$3\times 3$矩阵不能表示平移操作,就把其拓展到了$4\times 4$的矩阵。为此,还需要把原来的三维矢量转换成四维矢量,也就是**齐次坐标**。可以发现,齐次坐标并没有神秘的地方,它只是为了方便计算而使用的一种表示方式而已

  如上所说,齐次坐标是一个四维矢量。对于一个点,从三维坐标转换成齐次坐标是把 w w w分量设为1,而对于方向适量而言,需要把 w w w分量设为0。这样的设置会导致,当用一个 4 × 4 4\times 4 4×4矩阵对一个点进行变换时,平移、旋转、缩放都会施加于该点。但是如果是用于变换一个方向适量,平移的笑果就会被忽略

分解基础变换矩阵

  由于可以使用一个 4 × 4 4\times 4 4×4的矩阵来表示平移、旋转和缩放,可以把表示纯平移、纯旋转和纯缩放的变换矩阵叫做基础变换矩阵。这些矩阵具有一些共同点,可以把一个基础变换矩阵分解成4个组成部分

[ M 3 × 3 t 3 × 1 0 1 × 3 1 ] \begin{bmatrix} \mathbf{M_{3\times 3}} & \mathbf{t_{3\times 1}}\\ \mathbf{0_{1\times 3}}& 1\\ \end{bmatrix} [M3×301×3t3×11]

  其中,左上角的矩阵 M 3 × 3 \mathbf{M_{3\times 3}} M3×3用于表示旋转和缩放, t 3 × 1 \mathbf{t}_{3\times 1} t3×1用于表示平移, 0 1 × 3 \mathbf{0}_{1\times 3} 01×3是零矩阵,即 0 1 × 3 = [ 0 0 0 ] \mathbf{0}_{1\times 3}=\begin{bmatrix}0&0&0\end{bmatrix} 01×3=[000],右下角的元素就是标量1

平移矩阵

  可以使用矩阵乘法来表示对一个点进行平移变换:

[ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] [ x y z 1 ] = [ x + t x y + t y z + y z 1 ] \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+y_z\\ 1 \end{bmatrix} 100001000010txtytz1 xyz1 = x+txy+tyz+yz1

  从结果来看可以很容易看出为什么这个矩阵有平移的效果:点的 x 、 y 、 z x、y、z xyz分量分别增加了一个位置偏移。在3D中的可视化效果是,把点 ( x , y , z ) (x,y,z) (x,y,z)在空间中平移了 ( t x , t y , t z ) (t_x,t_y,t_z) (tx,ty,tz)个单位

  平移变换不会对方向矢量产生任何影响,因为矢量没有位置属性,也就是说它可以位于空间中的任意一点,因此对位置的改变(即平移)不应该对方向适量产生影响

  给定一个平移操作时如何构建一个平移矩阵?基础变换矩阵中的 t 3 × 1 \mathbf{t}_{3\times 1} t3×1矢量对应了平移矢量,左上角的矩阵 M 3 × 3 \mathbf{M}_{3\times 3} M3×3为单位矩阵 I 3 \mathbf{I}_3 I3

  平移矩阵的逆矩阵就是反向平移得到的矩阵,即

[ 1 0 0 − t x 0 1 0 − t y 0 0 1 − t z 0 0 0 1 ] \begin{bmatrix} 1&0&0&-t_x\\ 0&1&0&-t_y\\ 0&0&1&-t_z\\ 0&0&0&1 \end{bmatrix} 100001000010txtytz1

  可以看出,平移矩阵并不是一个正交矩阵

缩放矩阵

  可以对一个模型沿空间的 x x x轴, y y y轴和 z z z轴进行缩放。同样,可以使用矩阵乘法来表示一个缩放变换:

[ k x 0 0 0 0 k y 0 0 0 0 k z 0 0 0 0 1 ] [ x y z 1 ] = [ k x x k y y k z z 1 ] \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} kx0000ky0000kz00001 xyz1 = kxxkyykzz1

  对方向矢量可以使用同样的矩阵进行缩放:

[ k x 0 0 0 0 k y 0 0 0 0 k z 0 0 0 0 1 ] [ x y z 0 ] = [ k x x k y y k z z 0 ] \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\\ 0 \end{bmatrix}=\begin{bmatrix} k_xx\\ k_yy\\ k_zz\\ 0 \end{bmatrix} kx0000ky0000kz00001 xyz0 = kxxkyykzz0

  如果缩放系数 k x = k y = k z k_x=k_y=k_z kx=ky=kz,把这样的缩放称为统一缩放,否则称为非统一缩放。从外观上看,统一缩放是扩大整个模型,而非统一缩放会拉伸或挤压模型。更重要的是,统一缩放不会改变角度和比例信息,而非统一缩放会改变与模型相关的角度和比例。例如在对发现进行变换时,如果存在非统一缩放,直接使用用于变换顶点的变换矩阵的话,就会得到错误的结果

  缩放矩阵的逆矩阵时使用原缩放系数的倒数来对点和方向矢量进行缩放,即:

[ 1 k x 0 0 0 0 1 k y 0 0 0 0 1 k z 0 0 0 0 1 ] \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} kx10000ky10000kz100001

  缩放矩阵一般不是正交矩阵

  上面的矩阵只适用于沿坐标轴方向进行缩放。如果希望在任意方向上进行缩放,就需要使用复合变换。其中一个方法的主要思想就是,先将缩放轴变换成标准坐标轴,然后进行沿坐标轴的缩放,再使用逆变换得到原来的缩放轴朝向。

旋转矩阵

  旋转是三种常见的变换矩阵中最复杂的一种。

  如果需要把点绕着 x x x轴旋转 θ \theta θ度,可以使用下面的矩阵:

R x ( θ ) = [ 1 0 0 0 0 cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 ] \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} Rx(θ)= 10000cosθsinθ00sinθcosθ00001

  绕 y y y轴的旋转也是类似的:

R x ( θ ) = [ cos ⁡ θ 0 sin ⁡ θ 0 0 1 0 0 − sin ⁡ θ 0 cos ⁡ θ 0 0 0 0 1 ] \mathbf{R}_x(\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} Rx(θ)= cosθ0sinθ00100sinθ0cosθ00001

  最后,是绕 z z z轴的旋转:

R x ( θ ) = [ cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 0 0 0 0 1 ] \mathbf{R}_x(\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} Rx(θ)= cosθsinθ00sinθcosθ0000100001

  旋转矩阵的逆矩阵是旋转相反角度得到的变换矩阵。旋转矩阵是正交矩阵,而且多个旋转矩阵之间的串联同样是正交的

复合变换

  可以把平移、旋转和缩放组合起来,来形成一个复杂的变换过程。例如,可以对一个模型先进性大小为(2,2,2)的缩放,再绕 y y y轴旋转 3 0 ∘ 30^\circ 30,最后向 z z z轴平移4个单位。复合变换可以通过矩阵的串联来实现。上面的变换过程可以使用下面的公式来计算:

P n e w = M t r a n s l a t i o n M r o t a t i o n M s c a l e P o l d \mathbf{P}_{new}=\mathbf{M}_{translation}\mathbf{M}_{rotation}\mathbf{M}_{scale}\mathbf{P}_{old} Pnew=MtranslationMrotationMscalePold

  由于使用的是列矩阵,因此阅读顺序是从右到左,即先进行缩放变换,在进行旋转变换,最后进行平移变换。需要注意的是,变换的结果依赖于变换顺序的,由于矩阵乘法不满足交换律,因此矩阵的乘法顺序很重要。也就是说,不同的变换顺序得到的结果可能是不一样的。

  除了需要注意不同类型的变换顺序外,有时还需要小心旋转的变换顺序。

  当直接给出 ( θ x , θ y , θ z ) (\theta_x,\theta_y,\theta_z) (θx,θy,θz)这样的旋转角度时,需要定义一个旋转顺序。在Unity中,这个旋转的顺序时zxy,这在旋转相关的API文档中都有说明。这意味着,当给定 ( θ x , θ y , θ z ) (\theta_x,\theta_y,\theta_z) (θx,θy,θz)这样的旋转角度时,得到的顺序组合旋转变换矩阵是:

M r o t a t e z M r o t a t e x M r o t a t e y = [ cos ⁡ θ z − sin ⁡ θ z 0 0 sin ⁡ θ z cos ⁡ θ z 0 0 0 0 1 0 0 0 0 1 ] [ 1 0 0 0 0 cos ⁡ θ x − sin ⁡ θ x 0 0 sin ⁡ θ x cos ⁡ θ x 0 0 0 0 1 ] [ cos ⁡ θ y 0 sin ⁡ θ y 0 0 1 0 0 − sin ⁡ θ y 0 cos ⁡ θ y 0 0 0 0 1 ] {\mathbf{M}_{rotate}}_z{\mathbf{M}_{rotate}}_x{\mathbf{M}_{rotate}}_y=\begin{bmatrix} \cos\theta_z&-\sin\theta_z&0&0\\ \sin\theta_z&\cos\theta_z&0&0\\ 0&0&1&0\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} 1&0&0&0\\ 0&\cos\theta_x&-\sin\theta_x&0\\ 0&\sin\theta_x&\cos\theta_x&0\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} \cos\theta_y&0&\sin\theta_y&0\\ 0&1&0&0\\ -\sin\theta_y&0&\cos\theta_y&0\\ 0&0&0&1 \end{bmatrix} MrotatezMrotatexMrotatey= cosθzsinθz00sinθzcosθz0000100001 10000cosθxsinθx00sinθxcosθx00001 cosθy0sinθy00100sinθy0cosθy00001

  给定一个旋转顺序,以及它们对应的旋转角度 ( θ x , θ y , θ z ) (\theta_x,\theta_y,\theta_z) (θx,θy,θz),有两种坐标系可以选择

  • 绕坐标系E下的 z z z轴旋转 θ z \theta_z θz,绕坐标轴E下的 y y y轴旋转 θ y \theta_y θy,绕坐标系E下的 x x x轴旋转 θ x \theta_x θx,即进行一次旋转时不一起旋转当前坐标系
  • 绕坐标系E下的 z z z轴旋转 θ z \theta_z θz,在坐标系E下绕 z z z轴旋转 θ z \theta_z θz后的新坐标E’下的 y y y轴旋转 θ y \theta_y θy,在坐标系E’下绕 y y y轴旋转 θ y \theta_y θy后的新坐标系E’'下的 x x x轴旋转 x x x轴旋转 θ x \theta_x θx,即在旋转时,把坐标系一起转动

  很容易知道,这两种选择的结果是不一样的。但如果把它们的旋转顺序颠倒一下,它们得到的结果酒水是一样的。而Unity文档中说明的旋转顺序指的是在第一种情况下的顺序

坐标空间

坐标空间的转换

  想要定义一个坐标空间,必须指明其原点位置和3个坐标轴的方向。而这些数值实际上是相对于另一个坐标空间的(所有的都是相对的)。也就是说,坐标空间会形成一个层次结构——每个坐标空间都是另一个坐标空间的子空间,反过来说,每个空间都有一个坐标空间。对坐标空间的变换实际上就是在父空间和子空间之间对点和矢量进行变换

  假设,现在有坐标空间P以及一个子坐标空间C。可以知道在父坐标空间中子坐标空间的原点位置以及3个单位坐标轴。一般会有两种需求:一种需求是把子坐标空间下表示的点或矢量 A c \mathbf{A}_c Ac转换到父坐标下的表示 A p \mathbf{A}_p Ap,另一个需求是反过来,即把父坐标下表示的点或矢量 B p \mathbf{B}_p Bp转换到子坐标空间下的表示 B c \mathbf{B}_c Bc。可以使用下面的公式来表示这两种需求:

A p = M c − > p A c B c = M p − > c B p \mathbf{A}_p=\mathbf{M}_{c->p}\mathbf{A}_c\\ \mathbf{B}_c=\mathbf{M}_{p->c}\mathbf{B}_p Ap=Mc>pAcBc=Mp>cBp

  其中, M c − > p \mathbf{M}_{c->p} Mc>p表示的是从子坐标空间变换到父坐标空间的变换矩阵,而 M p − > c \mathbf{M}_{p->c} Mp>c是其逆矩阵(即反向变换)。那么,现在的问题就是如何求解这些变换矩阵。事实上,只需要解出二者之一即可,另一个矩阵可以通过求逆矩阵的方式来得到。

  下面,讲解如何求出从子坐标空间到父坐标空间的变换矩阵 M c − > p \mathbf{M}_{c->p} Mc>p

  首先回顾一个问题:给定一个坐标空间以及其中一点 ( a , b , c ) (a,b,c) (a,b,c)时,如何知道该点的位置?可以通过4个步骤来确定他的位置

  (1) 从坐标空间的原点开始
  (2) 向 x x x轴方向移动 a a a个单位
  (3) 向 y y y轴方向移动 b b b个单位
  (4) 向 z z z轴方向移动 z z z个单位

  注意,上面的步骤只是想想,这个点实际上并没有发生移动。现在,已知子坐标空间 C \mathbf{C} C的3个坐标轴在父坐标空间 P \mathbf{P} P下的表示 x c , y c , z c \mathbf{x}_c,\mathbf{y}_c,\mathbf{z}_c xc,yc,zc,以及其原点位置 O c \mathbf{O}_c Oc。当给定一个子坐标空间中的一点 A c = ( a , b , c ) \mathbf{A}_c=(a,b,c) Ac=(a,b,c),同样可以按照上面4个步骤来确定其在父坐标空间下的位置 A p \mathbf{A}_p Ap

  1. 从坐标空间的原点开始

  子坐标空间的原点位置 O c \mathbf{O}_c Oc

  2.向 x x x轴方向移动 a a a个单位

  因为知道了 x x x轴的矢量表示,因此可以得到

O c + a x c \mathbf{O}_c+a\mathbf{x}_c Oc+axc

  3.向 y y y轴方向移动 b b b个单位

  同样的道理可得:

O c + a x c + b y c \mathbf{O}_c+a\mathbf{x}_c+b\mathbf{y}_c Oc+axc+byc

  4.向 z z z轴方向移动移动 c c c个单位

  最后,可以得到:

O c + a x c + b y c + c z c \mathbf{O}_c+a\mathbf{x}_c+b\mathbf{y}_c+c\mathbf{z}_c Oc+axc+byc+czc

  最后就得到了 M c − > p \mathbf{M}_{c->p} Mc>p,通过变形即可得到与矩阵相关的表达式

A p = O c + a x c + b y c + c z c = ( x O c , y O c , z O c ) + a ( x x c , y x c , z x c ) + b ( x y c , y y c , z y c ) + c ( x z c , y z c , z z c ) = ( x O c , y O c , z O c ) + [ x x c y x c z x c x y c y y c z y c x z c y z c z z c ] [ a b c ] = ( x O c , y O c , z O c ) + [ ∣ ∣ ∣ x c y c z c ∣ ∣ ∣ ] [ a b c ] \begin{aligned} \mathbf{A}_p&=\mathbf{O}_c+a\mathbf{x}_c+b\mathbf{y}c+c\mathbf{z}_c\\ &=(x_{O_c},y_{O_c},z_{O_c})+a(x_{x_c},y_{x_c},z_{x_c})+b(x_{y_c},y_{y_c},z_{y_c})+c(x_{z_c},y_{z_c},z_{z_c})\\ &=(x_{O_c},y_{O_c},z_{O_c})+ \begin{bmatrix} x_{x_c}&y_{x_c}&z_{x_c}\\ x_{y_c}&y_{y_c}&z_{y_c}\\ x_{z_c}&y_{z_c}&z_{z_c} \end{bmatrix}\begin{bmatrix} a\\b\\c \end{bmatrix}\\ &=(x_{O_c},y_{O_c},z_{O_c})+\begin{bmatrix} |&|&|\\ \mathbf{x}_c&\mathbf{y}_c&\mathbf{z}_c\\ |&|&| \end{bmatrix}\begin{bmatrix} a\\b\\c \end{bmatrix} \end{aligned} Ap=Oc+axc+byc+czc=(xOc,yOc,zOc)+a(xxc,yxc,zxc)+b(xyc,yyc,zyc)+c(xzc,yzc,zzc)=(xOc,yOc,zOc)+ xxcxycxzcyxcyycyzczxczyczzc abc =(xOc,yOc,zOc)+ xcyczc abc

  其中“|”符号表示是按列展开的。上面的式子就是使用了之前所说的公式。但是最后这个表达式还存在加法表达式,即平移变换,因为 3 × 3 3\times 3 3×3无法表示平移变换,因此需要拓展到齐次坐标空间中,得

A p = ( x O c , y O c , z O c , 1 ) + [ ∣ ∣ ∣ 0 x c y c z c 0 ∣ ∣ ∣ 0 0 0 0 1 ] [ a b c 1 ] = [ 1 0 0 x o c 0 1 0 y o c 0 0 1 z o c 0 0 0 1 ] [ ∣ ∣ ∣ 0 x c y c z c 0 ∣ ∣ ∣ 0 0 0 0 1 ] [ a b c 1 ] = [ ∣ ∣ ∣ x o c x c y c z c y o c ∣ ∣ ∣ z o c 0 0 0 1 ] [ a b c 1 ] = [ ∣ ∣ ∣ ∣ x c y c z c O c ∣ ∣ ∣ ∣ 0 0 0 1 ] [ a b c 1 ] \begin{aligned} \mathbf{A}_p&=(x_{O_c},y_{O_c},z_{O_c},1)+\begin{bmatrix} |&|&|&0\\ \mathbf{x}_c&\mathbf{y}_c&\mathbf{z}_c&0\\ |&|&|&0\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} a\\b\\c\\1 \end{bmatrix}\\ &=\begin{bmatrix} 1&0&0&x_{o_c}\\ 0&1&0&y_{o_c}\\ 0&0&1&z_{o_c}\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} |&|&|&0\\ \mathbf{x}_c&\mathbf{y}_c&\mathbf{z}_c&0\\ |&|&|&0\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} a\\b\\c\\1 \end{bmatrix}\\ &=\begin{bmatrix} |&|&|&x_{o_c}\\ \mathbf{x}_c&\mathbf{y}_c&\mathbf{z}_c&y_{o_c}\\ |&|&|&z_{o_c}\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} a\\b\\c\\1 \end{bmatrix}\\ &=\begin{bmatrix} |&|&|&|\\ \mathbf{x}_c&\mathbf{y}_c&\mathbf{z}_c&\mathbf{O}_c\\ |&|&|&|\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} a\\b\\c\\1 \end{bmatrix} \end{aligned} Ap=(xOc,yOc,zOc,1)+ xc0yc0zc00001 abc1 = 100001000010xocyoczoc1 xc0yc0zc00001 abc1 = xc0yc0zc0xocyoczoc1 abc1 = xc0yc0zc0Oc1 abc1

  一旦求出来 M c − > p \mathbf{M}_{c->p} Mc>p M p − > c \mathbf{M}_{p->c} Mp>c就可以通过求逆矩阵的方式求出来,因为从坐标空间中 C \mathbf{C} C变换到坐标空间 P \mathbf{P} P变换到坐标空间 C \mathbf{C} C是互逆的两个过程

  可以看出来,变换矩阵 M c − > p \mathbf{M}_{c->p} Mc>p实际上可以通过坐标空间 C \mathbf{C} C在坐标空间 P \mathbf{P} P中的原点和坐标轴的矢量表示来构建出来:把3个坐标轴依次放入矩阵的前3列,把原点矢量放到最后一列,再用0和1填充最后一行即可

  需要注意的是,这里并没有要求3个坐标轴 x c , y c \mathbf{x}_c,\mathbf{y}_c xc,yc z c \mathbf{z}_c zc是单位矢量,事实上,如果存在缩放的话,这3个矢量值很可能不是单位矢量

  另外,还可以利用反向思维,从这个变换矩阵反推来获取子坐标空间的原点和坐标轴方向。例如,当知道从模型空间到世界空间的一个 4 × 4 4\times 4 4×4的变换矩阵,可以提取它的第一列再进行归一化后(为了消除缩放的影响)来得到模型空间的 x x x轴在世界空间下的单位矢量表示。同样的方法可以提取 y y y轴和 z z z轴。还可以从另一个角度来理解这个提取过程。因为矩阵 M c − > p \mathbf{M}_{c->p} Mc>p可以把一个方向矢量从坐标空间 C \mathbf{C} C变换到坐标空间 P \mathbf{P} P中,那么,只需要用它来变换坐标空间 C \mathbf{C} C中的 x x x轴(1,0,0,0),即使用矩阵乘法 M c > p [ 1 0 0 0 ] T \mathbf{M}_{c_>p}\begin{bmatrix}1&0&0&0\end{bmatrix}^T Mc>p[1000]T,得到的结果正式 M c − > p \mathbf{M}_{c->p} Mc>p的第一列

  因为矢量是没有位置的。因此坐标空间的原点变换是可以忽略的。也就是说,仅仅平移坐标系的原点是不会对矢量造成任何影响的。那么,对矢量的坐标空间变换就可以使用 3 × 3 3\times 3 3×3的矩阵来表示,因此不需要表示平移变换。那么变换矩阵就是:

M c − > p = [ ∣ ∣ ∣ x c y c z c ∣ ∣ ∣ ] \mathbf{M}_{c->p}=\begin{bmatrix} |&|&|\\ \mathbf{x}_c&\mathbf{y}_c&\mathbf{z}_c\\ |&|&| \end{bmatrix} Mc>p= xcyczc

  在Shader中,常常会看到截取变换矩阵的前3行前3列来对法线方向、光照方向来进行空间变换,这正是原因所在

  现在,再来关注 M p − > c \mathbf{M}_{p->c} Mp>c。前文提到可以通过求 M c − > p \mathbf{M}_{c->p} Mc>p的逆矩阵的方式求解出来反向变换 M c − > p \mathbf{M}_{c->p} Mc>p。但是有一种情况不需要求解逆矩阵就可以得到 M p − > c \mathbf{M}_{p->c} Mp>c,这种情况就是 M c − > p \mathbf{M}_{c->p} Mc>p是一个正交矩阵。如果它是一个正交矩阵的话, M c − > p \mathbf{M}_{c->p} Mc>p的逆矩阵就等于它的转置矩阵。这意味着不需要进行复杂的求逆操作就可以得到反向变换。也就是说:

M p − > c = [ ∣ ∣ ∣ x p y p z p ∣ ∣ ∣ ] = M c − > p − 1 = M c − > p T = [ − x c − − y c − − z c − ] \mathbf{M}_{p->c}=\begin{bmatrix} |&|&|\\ \mathbf{x}_p&\mathbf{y}_p&\mathbf{z}_p\\ |&|&| \end{bmatrix}=\mathbf{M}_{c->p}^{-1}=\mathbf{M}_{c->p}^T =\begin{bmatrix} -&\mathbf{x}_c&-\\ -&\mathbf{y}_c&-\\ -&\mathbf{z}_c&- \end{bmatrix} Mp>c= xpypzp =Mc>p1=Mc>pT= xcyczc

  现在不仅仅可以根据变换矩阵 M c − > p \mathbf{M}_{c->p} Mc>p反推出子坐标空间的坐标轴方向在父坐标空间中的表示 x c , y c \mathbf{x}_c,\mathbf{y}_c xc,yc z c \mathbf{z}_c zc,还可以反推出父坐标空间的坐标轴方向在子坐标空间中的表示 x p , y p \mathbf{x}_p,\mathbf{y}_p xp,yp z p \mathbf{z}_p zp,这些坐标轴对应的就是 M c − > p \mathbf{M}_{c->p} Mc>p的每一行。也就是说,如果已经知晓坐标空间变换矩阵 M A − > B \mathbf{M}_{A->B} MA>B是一个正交矩阵,那么就可以提取它的第一列来得到坐标空间 A \mathbf{A} A x x x轴在坐标空间 B \mathbf{B} B下的表示,还可以提取它的第一行来得到坐标空间 B \mathbf{B} B x x x轴在坐标空间 A \mathbf{A} A下的表示。反过来,如果已经知晓坐标空间 B \mathbf{B} B x x x轴、 y y y轴、 z z z轴(必须是单位矢量,否则构建出来的就不是正交矩阵)在坐标空间 A \mathbf{A} A下的表示,就可以把它们依次放在矩阵的每一行就可以得到从 A \mathbf{A} A B \mathbf{B} B的变换矩阵了

顶点的坐标空间变换过程

  之前提到,在渲染流水线中,一个顶点要经过多个坐标空间的变换才能最终被画在屏幕上。一个顶点最开始是在模型空间中定义的,最后它将会变换到屏幕空间中,得到真正的屏幕像素坐标。因此,下面的内容蒋介石顶点要进行的各种空间变换的过程

模型空间

  模型空间如他的名字所暗示的那样,是和某个模型或者说是对象有关的,有时模型空间也被称为对象空间局部空间。每个模型都有自己独立的坐标空间,当它移动或旋转的时候,模型空间也会跟着他移动和旋转。

  在模型空间中会经常使用一些方向概念,例如“前、后、左、右、上、下”。在本文中把这些方向称为自然方向。模型空间中的坐标轴通常会使用这些自然方向。前文讲过,Unity在模型空间中使用的是左手坐标系,因此在模型空间中, + x , + y , + z +x,+y,+z +x,+y,+z轴分别对应的是模型的右、上和前向。需要注意的是,模型空间中的 x x x轴, y y y轴, z z z轴和自然方向的对应不一定是上述这种关系,但由于Unity使用的的是这样的约定,因此本文将使用这种方式。可以在 Hierarchy 视图中单击任意对象就可以看见它们对应的模型空间的3个坐标轴

  模型空间的原点和坐标轴通常是由美术人员在建模软件里确定好的。当导入到Unity中后,可以在顶点着色器中访问到模型的定点信息,其中包含了每个顶点的坐标。这些坐标都是相对于模型空间中的原点(通常位于模型的重心)定义的

世界空间

  世界空间是一个特殊的坐标系,因为他建立了最大的空间,这里说的最大指的是一个宏观的概念,也就是说它是最外层的坐标空间

  世界空间可以被用于描述绝对位置。在本文中,绝对位置指的就是在世界坐标系中的位置。通常会把世界空间的原点位置在空间坐标的中心

  在Unity中,世界空间同样使用了左手坐标系。但它的 x , y , z x,y,z x,y,z轴是固定不变的。在Unity中可以通过调整 Transform 组件中的 Position 属性来改变模型的位置,这里的位置指的是相对于这个 Transform的父节点的模型坐标空间中的原点定义的。如果一个Transform没有任何父节点,那么这个位置就是在世界坐标系中的位置,如下图所示:

在这里插入图片描述
  顶点变换的第一步,就是将顶点坐标从模型空间变换到世界空间中。这个变换通常叫做模型变换

观察空间

  观察空间也被称为摄像机空间。观察空间可以认为是模型空间的一个特例——在所有的模型中有一个非常特殊的模型,即摄像机(虽然通常来说摄像机本身是不可见的),它的模型空间值得单独拿出来讨论,也就是观察空间

  摄像机决定了渲染游戏所使用的视角。在观察空间中,摄像机位于元代你,同样,其坐标轴的选择可以是任意的,而Unity中观察空间的坐标轴选择是: + x +x +x轴指向右方, + y +y +y指向上方, + z +z +z指向摄像机的后方。这是因为,Unity在模型空间和世界空间中选用的都是左手坐标系,而在观察空间中使用的是右手坐标系。这是符合OpenGL传统的,在这样的观察空间中,摄像机的正前方指向的是 − z -z z轴方向

  这种做右手坐标系之间的改变很少对Unity中的编程产生影响,因为Unity做了很多渲染的底层工作,包括很多坐标空间的转换,但是如果需要调用 Camera.cameraToWorldMatrix、Camera.worldToCameraMatrix 等接口自行计算某模型在观察空间中的位置就需要小心这样的差异

  最后,观察空间和屏幕空间是不同的。观察空间是一个三维空间,而屏幕空间是一个二维空间。从观察空间到屏幕空间的转换需要经过一个操作,那就是投影

  顶点变换的第二部,就是将顶点坐标从世界空间变换到观察空间中。这个变换通常叫做观察变换

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com