Unity Shader入门精要--第4 章 学习Shader 所需的数学基础:点和矢量

Unity系列文章目录

前言

点(point)是n 维空间(游戏中主要使用二维和三维空间)中的一个位置,它没有大小、宽度这类概念。在笛卡儿坐标系中,我们可以使用2 个或3 个实数来表示一个点的坐标,如:P=(Px,
Py),表示二维空间的点,P=(Px, Py,Pz)表示三维空间中的点。
矢量(vector,也被称为向量)的定义则复杂一些。在数学家看来,矢量就是一串数字。你
可能要问了,点的表达式不也是一串数字吗?没错,但矢量存在的意义更多是为了和标量(scalar)
区分开来。通常来讲,矢量是指n 维空间中一种包含了模(magnitude)和方向(direction)的有
向线段,我们通常讲到的速度(velocity)就是一种典型的矢量。例如,这辆车的速度是向南80km/h
(向南指明了矢量的方向,80km/h 指明了矢量的模)。而标量只有模没有方向,生活中常常说到
的距离(distance)就是一种标量。例如,我家离学校只有200 米(200 米就是一个标量)。
具体来讲。
 矢量的模指的是这个矢量的长度。一个矢量的长度可以是任意的非负数。
 矢量的方向则描述了这个矢量在空间中的指向。
矢量的表示方法和点类似。我们可以使用v=(x,y)来表示二维矢量,用v=(x,y,z)来表示三维矢
量,用v=(x,y,z,w)来表示四维矢量。
为了方便阐述,我们对不同类型的变量在书写和印刷上使用不同的样式:
 对于标量,我们使用小写字母来表示,如a,b,x,y,z,,等;
 对于矢量,我们使用小写的粗体字母来表示,如a,b,u,v 等;
 对于后面要学习的矩阵,我们使用大写的粗体字母来表示,如A,B,S,M,R 等。
在图4.15 中,一个矢量通常由一个箭头来表示。我们有时会讲到一个矢量的头(head)和尾
(tail)。矢量的头指的是它的箭头所在的端点处,而尾指的是另一个端点处,如图4.15 所示。
那么一个矢量要放在哪里呢?从矢量的定义来看,它只有模和方向两个属性,并没有位置信
息。这听起来很难理解,但实际上在生活中我们总是会和这样的矢量打交道。例如,当我们讲到
一个物体的速度时,可能会这样说“那个小偷正在以100km/h 的速度向南逃窜”(快抓住他!),
这里的“以100km/h 的速度向南”就可以使用一个矢量来表示。通常,矢量被用于表示相对于某
个点的偏移(displacement),也就是说它是一个相对量。只要矢量的模和方向保持不变,无论放
在哪里,都是同一个矢量。

点和矢量

4.3.1 点和矢量的区别

回顾一下,点是一个没有大小之分的空间中的位置,而矢量是一个有模和方向但没有位置的
量。从这里看,点和矢量具有不同的意义。但是,从表示方式上两者非常相似。
在上一节中我们提到,矢量通常用于描述偏移量,因此,它们可以用于描述相对位置,即相
对于另一个点的位置,此时矢量的尾是一个位置,那么矢量的头就可以表示另一个位置了。而一
个点可以用于指定空间中的一个位置(即相对于原点的位置)。如果我们把矢量的尾固定在坐标系
原点,那么这个矢量的表示就和点的表示重合了。图4.16 表示了两者之间的关系。
在这里插入图片描述
尽管上面的内容看起来显而易见,但区分点和矢量之间的不同是非常重要的,尽管它们在数
学表达式上是一样的,都是一串数字而已。如果一定要给它们之间建立一个联系的话,我们可以
认为,任何一个点都可以表示成一个从原点出发的矢量。为了明确点和矢量的区别,在本书后面
的内容中,我们将用于表示方向的矢量称为方向矢量。
4.3.2 矢量运算
在下面的内容里,我们将给出一些最常见的矢量运算。幸运的是,这些运算大都很好理解。
对于每种运算,我们会先给出数学上的描述,然后再给出几何意义上的解释。同样,为了让读者
加深印象,我们会在最后给出一些练习题。相信读完本节后,你一定可以快速地解决它们!
1.矢量和标量乘法/除法
还记得吗?标量是只有模没有方向的量,虽然我们不能把矢量和标量进行相加/相减的运算
(想象一下,你会把速度和距离相加吗),但可以对它们进行乘法运算,结果会得到一个不同长度
且可能方向相反的新的矢量。
公式非常简单,我们只需要把矢量的每个分量和标量相乘即可:
kv=(kvx,kvy, kvz)
类似的,一个矢量也可以被一个非零的标量除。这等同于和这个标量的倒数相乘:
v
k
 (x, y, z)
k
 1
k
(x, y, z)  x
k
, y
k
, z
k

 

 
,k  0
下面给出一些例子:
2(1,2,3)=(2,4,6)
−3.5(2, 0)=(−7, 0)
(1,2,3)
2
 (0.5,1,1.5)
注意,对于乘法来说,矢量和标量的位置可以互换。但对于除法,只能是矢量被标量除,而
不能是标量被矢量除,这是没有意义的。
从几何意义上看,把一个矢量v 和一个标量k 相乘,意味着对矢量v 进行一个大小为|k|的缩
放。例如,如果想要把一个矢量放大两倍,就可以乘以2。当k<0 时,矢量的方向也会取反。图
4.17 显示了这样的一些例子。
2.矢量的加法和减法
我们可以对两个矢量进行相加或相减,其结果是一个相同维度的新矢量。
我们只需要把两个矢量的对应分量进行相加或相减即可。公式如下:
a+b=(ax+bx,ay+by, az+bz)
a–b=(ax−bx,ay-by, az−bz)
下面是一些例子:
(1,2,3)+(4,5,6)=(5,7,9)
(5,2,7) − (3,8,4)=(2, −6,3)
需要注意的是,一个矢量不可以和一个标量相加或相减,或者是和不同维度的矢量进行运算。
从几何意义上来看,对于加法,我们可以把矢量a 的头连接到矢量b 的尾,然后画一条从a
的尾到b 的头的矢量,来得到a 和b 相加后的矢量。也就是说,如果我们从一个起点开始进行了
一个位置偏移a,然后又进行一个位置偏移b,那么就等同于进行了一个a+b 的位置偏移。这被称为矢量加法的三角形定则(triangle rule)。矢量的减法是类似的。如图4.18 所示。

在这里插入图片描述
读者需要时刻谨记,在图形学中矢量通常用于描述位置偏移(简称位移)。因此,我们可以利
用矢量的加法和减法来计算一点相对于另一点的位移。
假设,空间内有两点a 和b。还记得吗,我们可以用矢量a 和b 来表示它们相对于原点的位
移。如果我们想要计算点b 相对于点a 的位移,就可以通过把b 和a 相减得到,如图4.19 所示。
3.矢量的模
正如我们之前讲到的一样,矢量是有模和方向的。矢量的模是一个标量,可以理解为是矢量
在空间中的长度。它的表示符号通常是在矢量两旁分别加上一条垂直线(有的文献中会使用两条
垂直线)。三维矢量的模的计算公式如下:
|v|= 2 2 2
vx  vy  vz
其他维度的矢量的模计算类似,都是对每个分量的平方相加后再开根号得到。
下面给出一些例子:
| (1, 2,3) | 12  22  32  1 4  9  14  3.742
| (3, 4) | 32  42  9 16  25  5
我们可以从几何意义来理解上述公式。对于二维矢量来说,我们可以对任意矢量构建一个三
角形,如图4.20 所示。

在这里插入图片描述
从图4.20 可以看出,对于二维矢量,其实就是使用了勾股定理,矢量的两个分量的绝对值对
应了三角形两个直角边的长度,而斜边的长度就是矢量的模。
4.单位矢量
在很多情况下,我们只关心矢量的方向而不是模。例如,在计算光照模型时,我们往往需要
得到顶点的法线方向和光源方向,此时我们不关心这些矢量有多长。在这些情况下,我们就需要
计算单位矢量(unit vector)。
单位矢量指的是那些模为1 的矢量。单位矢量也被称为被归一化的矢量(normalized vector)。
对任何给定的非零矢量,把它转换成单位矢量的过程就被称为归一化(normalization)。
给定任意非零矢量v,我们可以计算和v 方向相同的单位矢量。在本书中,我们通过在一个
矢量的头上添加一个戴帽符号来表示单位矢量,例如^ v 。为了对矢量进行归一化,我们可以用矢
量除以该矢量的模来得到。公式如下:
^ =
| |
v v
v
,v 是任意非零矢量
下面给出一些例子:
(3,4)
| (3,4) |
 (3,4)
32  (4)2
 (3,4)
25
 (3,4)
5
 3
5
, 4
5

 

 
 (0.6,0.8)
零矢量(即矢量的每个分量值都为0,如v=(0,0,0))是不可以被归一化的。这是因为做除法
运算时分母不能为0。
从几何意义上看,对二维空间来说,我们可以画一个单位圆,
那么单位矢量就可以是从圆心出发、到圆边界的矢量。在三维空
间中,单位矢量就是从一个单位球的球心出发、到达球面的矢量。
图4.21 给出了二维空间内的一些单位矢量。
需要注意的是,在后面的章节中我们将会不断遇到法线方向
(也被称为法矢量)、光源方向等,这些矢量不一定是归一化后
的矢量。由于我们的计算往往要求矢量是单位矢量,因此在使用
前应先对这些矢量进行归一化运算。
5.矢量的点积
矢量之间也可以进行乘法,但是和标量之间的乘法有很大不同。矢量的乘法有两种最常用的
种类:点积(dot product,也被称为内积,inner product)和叉积(cross product,也被称为外
积,outer product)。在本节中,我们将讨论第一种类型:点积。
读者可能认为上面几节的内容都很简单,“这些都显而易见嘛”。那么从这一节开始,我们就
会遇到一些真正需要花费力气(真的只要一点点)去记忆的公式。幸运的是,绝大多数公式是有
几何意义的,也就是说,我们可以通过画图的方式来理解和帮助记忆。
比仅仅记住这些公式更加重要的是,我们要真正理解它们是做什么的。只有这样,我们才能
在需要时想起来,“噢,这个需求我可以用这个公式来实现!”在我们编写Shader 的过程中,通常
程序接口都会提供这些公式的实现,因此我们往往不需要手工输入这些公式。例如,在Unity Shader
中,我们可以直接使用形如dot(a, b)的代码来对两个矢量值进行点积的运算。
点积的名称来源于这个运算的符号:a·b。中间的这个圆点符号是不可以省略的。点积的公式
有两种形式,我们先来看第一种。两个三维矢量的点积是把两个矢量对应分量相乘后再取和,最
后的结果是一个标量。
公式一:
a·b=(ax, ay, az) ·(bx, by, bz)= axbx+ ayby+azbz

在这里插入图片描述
下面是一些例子:
(1,2,3) ·(0.5,4,2.5)=0.5+8+7.5=16
(−3,4,0)·(5, −1,7)= −15+−4+0=−19
矢量的点积满足交换律,即a·b=b·a
点积的几何意义很重要,因为点积几乎应用到了图形学的各个方面。其中一个几何意义就是
投影(projection)。
假设,有一个单位矢量^a 和另一个长度不限的矢量b。现在,我们希望得到b 在平行于^a 的
一条直线上的投影。那么,我们就可以使用点积^a ·b 来得到b 在^a 方向上的有符号的投影。
那么,投影到底是什么意思呢?这里给出一个通俗的解释。我们可以认为,现在有一个光源,
它发出的光线是垂直于^a 方向的,那么b 在^a 方向上的投影就是b 在^a 方向上的影子,如图4.22
所示。
需要注意的是,投影的值可能是负数。投影结果的正负号与^a 和b 的方向有关:当它们的方
向相反(夹角大于90°)时,结果小于0;当它们的方向互相垂直(夹角为90°)时,结果等于0;
当它们的方向相同(夹角小于90°)时,结果大于0。图4.23 给出了这3 种情况的图示。
在这里插入图片描述
也就是说,点积的符号可以让我们知道两个矢量的方向关系。
那么,如果^a 不是一个单位矢量会如何呢?这很容易想到,任何两个矢量的点积a·b 等同于b
在a 方向上的投影值,再乘以a 的长度。
点积具有一些很重要的性质,在Shader 的计算中,我们会经常利用这些性质来帮助计算。
性质一:点积可结合标量乘法。
上面的“结合”是说,点积的操作数之一可以是另一个运算的结果,即矢量和标量相乘的结
果。公式如下:
(ka)·b= a·(kb)=k(a·b)
也就是说,对点积中其中一个矢量进行缩放的结果,相当于对最后的点积结果进行缩放。
性质二:点积可结合矢量加法和减法,和性质一类似。
这里的“结合”指的是,点积的操作数可以是矢量相加或相减后的结果。用公式表达就是:
a·(b+ c)= a·b+ a·c
把上面的c 换成−c 就可以得到减法的版本。
性质三:一个矢量和本身进行点积的结果,是该矢量的模的平方。
这点可以很容易从公式验证得到:
v·v=vxvx+ vyvy+ vzvz=|v|2
这意味着,我们可以直接利用点积来求矢量的模,而不需要使用模的计算公式。当然,我们
需要对点积结果进行开平方的操作来得到真正的模。但很多情况下,我们只是想要比较两个矢量
的长度大小,因此可以直接使用点积的结果。毕竟,开平方的运算需要消耗一定性能。
现在是时候来看点积的另一种表示方法了。这种方法是从三角代数的角度出发的,这种表示
方法更加具有几何意义,因为它可以明确地强调出两个矢量之间的角度。
我们先直接给出第二个公式。
公式二:
a·b=|a||b|cos
初看之下,似乎和公式一没有什么联系,怎么会相等呢?我们先来看最简单的情况。假设,
我们对两个单位矢量进行点积,即^a · ^b ,如图4.24 所示。
到了产生魔法的时间了!我们知道^ b 的模为1,且读者应
该记得cos =
邻边
斜边
。我们可以发现,图中^a · ^ b 的结果刚好就
是cos 对应的直角边。因此,由图4.24 可以得到:
^a · ^ b =
邻边
斜边
=cos
这也就是说,两个单位矢量的点积等于它们之间夹角的
余弦值。再应用性质一就可以得到公式二了:
a·b=(|a| ^a )·(|b| ^ b )=|a||b|( ^a · ^ b )=|a||b|cos
也就是说,两个矢量的点积可以表示为两个矢量的模相乘,再乘以它们之间夹角的余弦值。
从这个公式也可以看出,为什么计算投影时两个矢量的方向不同会得到不同符号的投影值:当夹
角小于90°时,cos>0;当夹角等于90°时,cos=0;当夹角大于90°时,cos<0。
利用这个公式我们还可以求得两个向量之间的夹角(在0°~180°):
=arcos( ^a · ^ b ),假设^a 和^ b 是单位矢量。
其中,arcos 是反余弦操作。
6.矢量的叉积
另一个重要的矢量运算就是叉积(cross product),也被称为外积(outer product)。与点积
不同的是,矢量叉积的结果仍是一个矢量,而非标量。
和点积类似,叉积的名称来源于它的符号:a×b。同样,这个叉号也是不可省略的。两个矢
量的叉积可以用如下公式计算:
a×b=(ax, ay, az)×(bx, by, bz)=(aybz−azby, azbx−axbz, axby−aybx)
上面的公式看起来很复杂,但其实是有一定规律的。图4.25 给出了这样的规律图示。
▲图4.25 三维矢量叉积的计算规律。不同颜色的线表示了计算结果矢量中对应颜色的分量的计算路径。
以红色为例,即结果矢量的第一个分量,它是从第一个矢量的y 分量出发乘以第二个矢量的z 分量,
再减去第一个矢量的z 分量和第二矢量的y 分量的乘积

在这里插入图片描述
在这里插入图片描述
例如:
(1,2,3)×(−2, −1,4)=((2)(4) − (3)(−1),(3)(−2) − (1)(4),(1)(−1)−(2)(−2))
=(8−(−3),(−6)−4,(−1)−(−4))=(11,−10,3)
需要注意的是,叉积不满足交换律,即a×b≠b×a。实际上,叉积是满足反交换律的,即
a×b=−(b×a)。而且叉积也不满足结合律,即 (a×b) ×c≠a×(b×c)。
从叉积的几何意义出发,我们可以更加深入地理解它的用处。对两个矢量进行叉积的结果会
得到一个同时垂直于这两个矢量的新矢量。我们已经知道,矢量是由一个模和方向来定义的,那
么这个新的矢量的模和方向是什么呢?
我们先来看它的模。a×b 的长度等于a 和b 的模的乘积再乘以它们之间夹角的正弦值。公式
如下:
|a×b|=|a||b|sin
读者可能已经发现,上述公式和点积的计算公式很类似,不同的是,这里使用的是正弦值。
如果读者对中学数学还有记忆的话,可能还会发现,这和平行四边形的面积计算公式是一样的。
如果你忘记了,没关系,我们在这里回忆一下。
如图4.26 所示,我们使用a 和b 构建一个平行四边形。
我们知道,平行四边形的面积可以使用|b|h 来得到,即底乘以高。而h 又可以使用|a|和夹角
来得到,即
A=|b|h=|b|(|a|sin)=|a||b|sin=|a×b|
你可能会问,如果a 和b 平行(可以是方向完全相同,也可以是完全相反)怎么办,不就不
能构建平行四边形了吗?我们可以认为构建出来的平行四边形面积为0,那么a×b=0。注意,这
里得到的是零向量,而不是标量0。
下面,我们来看结果矢量的方向。你可能会说:“方向?不是已经说了方向了嘛,就是和两个
矢量都垂直就可以了啊。”但是,如果你仔细想一下就会发现,实际上我们有两个方向可以选择,
这两个方向都和这两个矢量垂直。那么,我们要选择哪个方向呢?
这里就要和之前提到的左手坐标系和右手坐标系联系起来了,如图4.27 所示。
在这里插入图片描述
这个结果是怎么得到的呢?来,举起你的双手!哦,不……先举起你的右手。在右手坐标系
中,a×b 的方向将使用右手法则来判断。我们先想象把手心放在了a 和b 的尾部交点处,然后张
开你的手掌让手掌方向和a 的方向重合,再弯曲你的四指让它们向b 的方向靠拢,最后伸出你的
大拇指!大拇指指向的方向就是右手坐标系中a×b 的方向了。如果你实在不明白怎么摆放和扭动
你的手,那么就看图4.28 好了。

同理,我们可以使用左手法则来判断左手坐标系中
a×b 的方向。赶紧举起你的左手试试吧(你可能会发现
这个姿势比较扭曲)!
需要注意的是,虽然看起来左右手坐标系的选择会
影响叉积的结果,但这仅仅是“看起来”而已。从叉积
的数学表达式可以发现,使用左手坐标系还是右手坐标
系不会对计算结果产生任何影响,它影响的只是数字在
三维空间中的视觉化表现而已。当从右手坐标系转换为
左手坐标系时,所有点和矢量的表达和计算方式都会保
持不变,只是当呈现到屏幕上时,我们可能会发现,“咦,
怎么图像反过来了!”。当我们想要两个坐标系达到同样
的视觉效果时,可能就需要改变一些数学运算公式,这
不在本书的范畴内。有兴趣的读者可以参考本章的扩展
阅读部分。
那么,叉积到底有什么用呢?最常见的一个应用就是计算垂直于一个平面、三角形的矢量。
另外,还可以用于判断三角面片的朝向。读者可以在本节的练习题中找到这些应用。

在这里插入图片描述

参考

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值