Unity Shader入门精要--第4 章 学习Shader 所需的数学基础:矩阵

本文详细介绍了矩阵的基本概念,包括矩阵的定义、矩阵与矢量的关系、矩阵运算,特别是矩阵乘法的规则。同时,讲解了单位矩阵、转置矩阵、逆矩阵和正交矩阵等特殊矩阵的性质及其在Unity中的应用,强调了它们在三维空间变换中的重要性。
摘要由CSDN通过智能技术生成

Unity系列文章目录

前言

不幸的是,没有人能告诉你母体(matrix)究竟是什么。你需要自己去发现它。
—电影《黑客帝国》(英文名:The Matrix)
矩阵,英文名是matrix。如果你用翻译软件去查matrix 这个单词的翻译,就会发现它还有一
个意思就是母体。事实上,很多人都不知道,那部具有跨时代意义的电影《黑客帝国》的英文名
就是《The Matrix》。在电影《黑客帝国》中,母体是一个庞大的虚拟系统,它看似虚无缥渺,但
又连接万物。这一点和矩阵有异曲同工之妙。
没有人敢否认矩阵在三维数学中的重要性,事实上矩阵在整个线性代数的世界中都扮演了举
足轻重的角色。在三维数学中,我们通常会使用矩阵来进行变换。一个矩阵可以把一个矢量从一
个坐标空间转换到另一个坐标空间。在第2 章渲染流水线中,我们就看到了很多坐标变换,例如
在顶点着色器中我们需要把顶点坐标从模型空间变换到齐次裁剪坐标系中。而在这一章中,我们
先来认识一下矩阵这个概念。
那么,现在我们就来看一下,这些放在一个小括号里的数字怎么就这么重要呢?为什么数学
家们都喜欢用这个小东西来搞出这么多名堂呢?
4.4.1 矩阵的定义
相信很多读者都见过矩阵的真容,例如像下面这个样子:
在这里插入图片描述
从它的外观上来看,就是一个长方形的网格,每个格子里放了一个数字。的确,矩阵就是这
么简单:它是由m×n 个标量组成的长方形数组。在上面的式子中,我们是用方括号来围住矩阵
中的数字,而一些其他的资料可能会使用圆括号或花括号来表示,这都是等价的。
既然是网格结构,就意味着矩阵有行(row)列(column)之分。例如上面的例子就是一个3×4
的矩阵,它有三行四列。据此,我们可以给出矩阵的一般表达式。以3×3 的矩阵为例,它可以写成:
在这里插入图片描述
mij 表明了这个元素在矩阵M 的第i 行、第j 列。
这样看起来矩阵也没什么神秘的嘛。但是,越简单的东西往往越厉害,这也是数学的魅力所在。
4.4.2 和矢量联系起来
前面说到,矢量其实就是一个数组,而矩阵也是一个数组。既然都是数组,那就是一家人了!
我们很容易想到,我们可以用矩阵来表示矢量。实际上,矢量可以看成是n×1 的列矩阵(column
matrix)或1×n 的行矩阵(row matrix),其中n 对应了矢量的维度。例如,矢量v=(3,8,6)可以写
成行矩阵

在这里插入图片描述
为什么我们要把矢量和矩阵联系在一起呢?这是为了可以让矢量像一个矩阵一样一起参与矩
阵运算。这在空间变换中将非常有用。
到现在,使用行矩阵还是列矩阵来表示矢量看起来是没什么分别的。的确,我们可以根据自
己的喜好来选择表示方法,但是,如果要和矩阵一起参与乘法运算时,这种选择会影响我们的书
写顺序和结果。这正是我们下面要讲到的。

4.4.3 矩阵运算

矩阵这个家伙看起来比矢量要庞大很多,那么它的运算是不是很复杂呢?答案是肯定的。但
是,幸运的是在写Shader 的过程中,我们只需要和很简单的一部分运算打交道。
1.矩阵和标量的乘法
和矢量类似,矩阵也可以和标量相乘,它的结果仍然是一个相同维度的矩阵。它们之间的乘
法非常简单,就是矩阵的每个元素和该标量相乘。以3×3 的矩阵为例,其公式如下:
在这里插入图片描述
2.矩阵和矩阵的乘法
两个矩阵的乘法也很简单,它们的结果会是一个新的矩阵,并且这个矩阵的维度和两个原矩
阵的维度都有关系。
一个r×n 的矩阵A 和一个n×c 的矩阵B 相乘,它们的结果AB 将会是一个r×c 大小的矩阵。
请读者注意它们的行列关系,第一个矩阵的列数必须和第二个矩阵的行数相同,它们相乘得到的
矩阵的行数是第一个矩阵的行数,而列数是第二个矩阵的列数。例如,如果矩阵A 的维度是4×3,
矩阵B 的维度是3×6,那么AB 的维度就是4×6。
如果两个矩阵的行列不满足上面的规定怎么办?那么很抱歉,这两个矩阵就不能相乘,因为
它们之间的乘法是没有被定义的(当然,读者完全可以自己定义一种新的乘法,但是数学家们会
不会买账就不一定了)。那么为什么会有上面的规定呢?等我们理解了矩阵乘法的操作过程自然就
会明白。
我们先给出看起来很复杂难懂(当给出直观的表式后读者会发现其实它没那么难懂)的数学
表达式:设有r×n 的矩阵A 和一个n×c 的矩阵B,它们相乘会得到一个r×c 的矩阵C=AB。那
么,C 中的每一个元素cij 等于A 的第i 行所对应的矢量和B 的第j 列所对应的矢量进行矢量点乘
的结果,即
cij=ai1b1j+ ai2b2j+…+ ainbnj=
1
n
ik kj
k
a b
 
看起来很复杂对吗?但是,我们可以用一个更简单的方式来解释:对于每个元素cij,我们找
到A 中的第i 行和B 中的第j 列,然后把它们的对应元素相乘后再加起来,这个和就是cij。
一种更直观的方式如图4.30 所示。假设A 的大小是4×2,B 的大小是2×4,那么如果要计算
C 的元素c23 的话,先找到对应的行矩阵和列矩阵,即
A 中的第2 行和B 中的第3 列,把它们进行矢量点积后
就可以得到结果值。因此, c23=a21b13+a22b23。
在Shader 的计算中,我们更多的是使用4×4 矩阵
来运算的。
矩阵乘法满足一些性质。
性质一:矩阵乘法并不满足交换律。
也就是说,通常情况下:
AB≠BA
性质二:矩阵乘法满足结合律。
也就是说,
(AB)C=A(BC)
矩阵乘法的结合律可以扩展到更多矩阵的相乘。例如,
ABCDE=((A(BC))D)E=(AB)(CD)E
读者可根据矩阵乘法的定义很轻松地验证上述结论。
▲图4.30 计算c23 的过程
在这里插入图片描述

4.4.4 特殊的矩阵

有一些特殊的矩阵类型在Shader 中经常见到。这些特殊的矩阵往往具有一些重要的性质。
1.方块矩阵
方块矩阵(square matrix),简称方阵,是指那些行和列数目相等的矩阵。在三维渲染里,最
常使用的就是3×3 和4×4 的方阵。
方阵之所以值得单独拿出来讲,是因为矩阵的一些运算和性质是只有方阵才具有的。例如,
对角元素(diagonal elements)。方阵的对角元素指的是行号和列号相等的元素,例如m11、m22、
m33 等。如果把方阵看成一个正方形的话,这些元素排列在正方形的对角线上,这也是它们名字
的由来。如果一个矩阵除了对角元素外的所有元素都为0,那么这个矩阵就叫做对角矩阵(diagonal
matrix)。例如,下面就是一个4×4 的对角矩阵:
在这里插入图片描述

2.单位矩阵

一个特殊的对角矩阵是单位矩阵(identity matrix),用In 来表示。一个3×3 的单位矩阵如下:

在这里插入图片描述
为什么要为这种矩阵单独起一个名字呢?这是因为,任何矩阵和它相乘的结果都还是原来的
矩阵。也就是说,
在这里插入图片描述

这就跟标量中的数字1 一样!
3.转置矩阵
转置矩阵(transposed matrix)实际是对原矩阵的一种运算,即转置运算。给定一个r×c 的
矩阵M,它的转置可以表示成MT,这是一个c×r 的矩阵。转置矩阵的计算非常简单,我们只需
要把原矩阵翻转一下即可。也就是说,原矩阵的第i 行变成了第i 列,而第j 列变成了第j 行。数
学公式是:

在这里插入图片描述

对于行矩阵和列矩阵来说,我们可以使用转置操作来转换行列矩阵:
在这里插入图片描述

转置矩阵也有一些常用的性质。
性质一:矩阵转置的转置等于原矩阵。
很容易理解,我们把一个矩阵翻转一下后再翻转一下,等于没有对矩阵做任何操作。即
(MT)T=M
性质二:矩阵串接的转置,等于反向串接各个矩阵的转置。
用公式表示就是:
(AB)T=BTAT
该性质同样可以扩展到更多矩阵相乘的情况。
4.逆矩阵
逆矩阵(inverse matrix)大概是本书讲到的关于矩阵最复杂的一种操作了。不是所有的矩阵
都有逆矩阵,第一个前提就是,该矩阵必须是一个方阵。
给定一个方阵M,它的逆矩阵用M−1 来表示。逆矩阵最重要的性质就是,如果我们把M 和
M-1 相乘,那么它们的结果将会是一个单位矩阵。也就是说,
MM−1= M−1M=I
前面说了,并非所有的方阵都有对应的逆矩阵。一个明显的例子就是一个所有元素都为0 的
矩阵,很显然,任何矩阵和它相乘都会得到一个零矩阵,即所有的元素仍然都是0。如果一个矩
阵有对应的逆矩阵,我们就说这个矩阵是可逆的(invertible)或者说是非奇异的(nonsingular);
相反的,如果一个矩阵没有对应的逆矩阵,我们就说它是不可逆的(noninvertible)或者说是奇
异的(singular)。
那么如何判断一个矩阵是否是可逆的呢?简单来说,如果一个矩阵的行列式(determinant)
不为0,那么它就是可逆的。关于矩阵的行列式是什么以及如何求解一个矩阵的逆矩阵,可以参
见本章的扩展阅读部分。由于这部分内容涉及较多计算和其他定义,本书不再赘述。在写Shader
的过程中,这些矩阵通常可以通过调用第三方库(如C++数学库Eigen)来直接求得,不需要开
发者手动计算。在Unity 中,重要变换矩阵的逆矩阵Unity 也提供了相应的变量供我们使用。关
于这些Unity 内置的矩阵,读者可以在本章的4.8 节找到更详细的解释。
逆矩阵有很多非常重要的性质。
性质一:逆矩阵的逆矩阵是原矩阵本身。
假设矩阵M 是可逆的,那么
(M−1)−1=M
性质二:单位矩阵的逆矩阵是它本身。

I−1=I

性质三:转置矩阵的逆矩阵是逆矩阵的转置。

(MT)−1=(M−1)T
性质四:矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵。

(AB)−1=B−1A−1
这个性质也可以扩展到更多矩阵的连乘,如:
(ABCD)−1=D−1C−1B−1A−1
逆矩阵是具有几何意义的。我们知道一个矩阵可以表示一个变换(详见4.5 节),而逆矩阵允
许我们还原这个变换,或者说是计算这个变换的反向变换。因此,如果我们使用变换矩阵M 对矢
量v 进行了一次变换,然后再使用它的逆矩阵M−1 进行另一次变换,那么我们会得到原来的矢量。
这个性质可以使用矩阵乘法的结合律很容易地进行证明:
M−1(Mv)=(M−1M)v=Iv=v
5.正交矩阵
另一个特殊的方阵是正交矩阵(orthogonal matrix)。正交是矩阵的一种属性。如果一个方阵
M 和它的转置矩阵的乘积是单位矩阵的话,我们就说这个矩阵是正交的(orthogonal)。反过来也
是成立的。也就是说,矩阵M 是正交的等价于:
MMT= MTM=I
读者可能已经看出来,上式和我们在上一节讲到的逆矩阵时遇到的公式很像。把这两个公式
结合起来,我们就可以得到一个重要的性质,即如果一个矩阵是正交的,那么它的转置矩阵和逆
矩阵是一样的。也就是说,矩阵M 是正交的等价于:
MT=M−1
这个式子非常有用,因为在三维变换中我们经常会需要使用逆矩阵来求解反向的变换。而逆
矩阵的求解往往计算量很大,但转置矩阵却非常容易求解:我们只需要把矩阵翻转一下就可以了。
那么,我们如何提前判断一个矩阵是否是正交矩阵呢?读者可能会说,判断MMT=I 是否成立就
可以了嘛!但是,求解这样一个表达式无疑是需要一定计算量的,这些计算量可能和直接求解逆
矩阵无异。而且,如果我们判断出来这不是一个正交矩阵,那么这些花在验证是否是正交矩阵上
的计算就浪费了。因此,我们更想不需要计算,而仅仅根据一个矩阵的构造过程来判断这个矩阵
是否是正交矩阵。为此,我们需要来了解正交矩阵的几何意义。
我们来看一下对于3×3 的正交矩阵有什么特点。根据正交矩阵的定义,我们有:
在这里插入图片描述
这样,我们就有了9 个等式:
在这里插入图片描述

我们可以得到以下结论:
 矩阵的每一行,即c1、c2 和c3 是单位矢量,因为只有这样它们与自己的点积才能是1;
 矩阵的每一行,即c1、c2 和c3 之间互相垂直,因为只有这样它们之间的点积才能是0。
 上述两条结论对矩阵的每一列同样适用,因为如果M是正交矩阵的话,MT 也会是正交矩阵。
也就是说,如果一个矩阵满足上面的条件,那么它就是一个正交矩阵。读者可以注意到,一
组标准正交基(定义详见4.2.2 节)可以精确地满足上述条件。在4.6.2 节中,我们会使用坐标空
间的基矢量来构建用于空间变换的矩阵。因此,如果这些基矢量是一组标准正交基的话(例如只
存在旋转变换),那么我们就可以直接使用转置矩阵来求得该变换的逆变换。
读者:我被标准正交、正交这些概念搞混了,可以再说明一下是什么意思吗?
我们:读者应该已经知道,一个坐标空间需要指定一组基矢量,也就是我们理解的坐标轴。
如果这些基矢量之间是互相垂直的,那么我们就把它们称为是一组正交基(orthogonal basis)。
但是,它们的长度并不要求一定是1。如果它们的长度的确是1 的话,我们就说它们是一组标准
正交基(orthonormal basis)。因此,一个正交矩阵的行和列之间分别构成了一组标准正交基。但
是,如果我们使用一组正交基来构建一个矩阵的话,这个矩阵可能就不是一个正交矩阵,因为这
些基矢量的长度可能不为1,也就是说它们不是标准正交基。
4.4.5 行矩阵还是列矩阵
我们已经了解了足够多的数学概念,但在学习矩阵的几何意义之前,我们有必要说明一下行
矩阵和列矩阵的问题。
在前面的章节中我们讲到,可以把一个矢量转换成一个行矩阵或是列矩阵。它们本身是没有
区别的,但是,当我们需要把它和另一个矩阵相乘时,就会出现一些差异。
假设有一个矢量v=(x,y,z),我们可以把它转换成行矩阵v= [xyz]或列矩阵v= [x y z]T(这里使
用了转置符号来避免列矩阵在我们的这一行中显得太高)。现在,有另一个矩阵M:

在这里插入图片描述
那么M 分别和行矩阵以及列矩阵相乘后会是什么结果呢?我们先来看M 和行矩阵的相乘。
由矩阵乘法的定义可知,我们需要把行矩阵放在M 的左边(还记得吗,矩阵乘法要求两个矩阵的
行列数满足一定条件),即
11 21 31 12 22 32 13 23 33 vM = [xm  ym  zm xm  ym  zm xm  ym  zm ]
而如果和列矩阵相乘的话,结果是:
在这里插入图片描述

读者认真对比就会发现,结果矩阵除了行列矩阵的区别外,里面的元素也是不一样的。这就
意味着,在和矩阵相乘时选择行矩阵还是列矩阵来表示矢量是非常重要的,因为这决定了矩阵乘
法的书写次序和结果值。

在Unity 中,常规做法是把矢量放在矩阵的右侧,即把矢量转换成列矩阵来进行运算。因此,
在本书后面的内容中,如无特殊情况,我们都将使用列矩阵。这意味着,我们的矩阵乘法通常都
是右乘,例如:
CBAv  (C(B(Av)))
使用列向量的结果是,我们的阅读顺序是从右到左,即先对v 使用A 进行变换,再使用B 进
行变换,最后使用C 进行变换。
上面的计算等价于下面的行矩阵运算:
vATBTCT  (((vAT )BT )CT )

参考

Unity Shader入门精要
作者:冯乐乐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值