Unity系列文章目录


文章目录

前言

计算机图形学之所以深奥难懂,很大原因是在于它是建立在虚拟世界上的数学模型。数学渗
透到图形学的方方面面,当然也包括Shader。在学习Shader 的过程中,我们最常使用的就是矢量
和矩阵(即数学的分支之一—线性代数)。
很多读者认为图形学中的数学复杂难懂。的确,一些数学模型在初学者看来晦涩难懂。但很
多情况下,我们需要打交道的只是一些基础的数学运算,而只要掌握了这些内容,就会发现很多
事情可以迎刃而解。我们在研究和学习他人编写的Shader 代码时,也不再会疑问:“他为什么要
这么写”,而是“哦,这里就是使用矩阵进行了一个变换而已。”
为了让读者能够参与到计算中来,而不是填鸭式地阅读,在一些小节的最后我们会给出一些
练习题。练习题的答案会在本章最后给出(不要偷看答案!)。需要注意的是,这些练习题并不是
可有可无的,我们并非想利用题海战术来让读者掌握这些数学运算,而是想利用这些练习题来阐
述一些容易出错或实践中常见的问题。通过这些练习题,读者可以对本节内容有更加深刻的理解。
那么,拿起笔来,让我们一起走进数学的世界吧!

4.1 背景:农场游戏

为了让读者更加理解数学计算的几何意义,我们先来假定一个场景。现在,假设我们正在开
发一款卡通风格的农场游戏。在这个游戏里,玩家可以在农场里养很多可爱的奶牛。与普通农场
游戏不同的是,我们的主角不是玩家,而是一头牛—妞妞,如图4.1 所示。妞妞不仅长得壮,
而且它对很多事情都充满了好奇心。
Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_学习Shader数学基础
读者:为什么游戏主角不是玩家呢?我们:因为我们的策划就是这么任性。
在故事的一开始,农场世界是没有数学概念的。通过下面的学习,我们会见证数学给这个世
界带来了怎样翻天覆地的变化。

4.2 笛卡儿坐标系

在游戏制作中,我们使用数学绝大部分都是为了计算位置、距离和角度等变量。而这些计算
大部分都是在笛卡儿坐标系(Cartesian Coordinate System)下进行的。这个名字来源于法国伟
大的哲学家、物理学家、心理学家、数学家笛卡儿(René Descartes)。
那么,我们为什么需要笛卡儿坐标系呢?有这样一个传说,讲述了笛卡儿提出笛卡儿坐标系
的由来。笛卡儿从小体弱多病,所以他所在的寄宿学校的老师允许他可以一直留在床上直到中午。
在笛卡儿的一生中,他每天的上午时光几乎都是在床上度过的。笛卡儿并没有把这段时间用在睡
懒觉上,而是思考了很多关于数学和哲学上的问题。有一天,笛卡儿发现一只苍蝇在天花板上爬
来爬去,他观察了很长一段时间。笛卡儿想:我要如何来描述这只苍蝇的运动轨迹呢?最后,笛
卡儿意识到,他可以使用这只苍蝇距离房间内不同
墙面的位置来描述,如图4.2 所示。他从床上起身,
写下了他的发现。然后,他试图描述一些点的位置,
正如他要描述苍蝇的位置一样。最后,笛卡儿就发
明了这个坐标平面。而这个坐标平面后来逐渐发
展,就形成了坐标系系统。人们为了纪念笛卡儿的
工作,就用他的名字来给这种坐标系进行命名。
当然,上面传说的可靠性无从验证。一些较真
儿的读者就不用急着向本书勘误邮箱中发邮件说:
“嘿,你简直是胡说!”不过,读者可以从这个传说
中发现,笛卡儿坐标系和我们的生活是密切相关的。
4.2.1 二维笛卡儿坐标系
事实上,读者很可能一直在用二维笛卡儿坐标系,尽管你可能并没有听过笛卡儿这个名词。
你还记得在《哈利波特与魔法石》电影中,哈利和罗恩大战奇洛教授的魔法棋盘吗?这里的国际
象棋棋盘也可以理解成是一个二维的笛卡儿坐标系。
图4.3 显示了一个二维笛卡儿坐标系。它是不是很像一个棋盘呢?
一个二维的笛卡儿坐标系包含了两个部分的信息:
 一个特殊的位置,即原点,它是整个坐标系的中心;
 两条过原点的互相垂直的矢量,即x 轴和y 轴。这些坐
标轴也被称为是该坐标系的基矢量。
虽然在图4.3 中x 轴和y 轴分别是水平和垂直方向的,但这
并不是必须的。想象把上面的坐标系整体向左旋转30°。而且,
虽然图中的x 轴指向右、y 轴指向上,但这也并不是必须的。例
如,在2.3.4 节屏幕映射中,OpenGL 和DirectX 使用了不同的二
维笛卡儿坐标系,如图4.4 所示。
而有了这个坐标系我们就可以精确地定位一个点的位置。例
如,如果说:“在(1, 2)的位置上画一个点。”那么相信读者肯定知道这个位置在哪里。
Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_坐标轴_02
Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_数学模型_03

我们来看一下笛卡儿坐标系给奶牛农场带来了什么变化。在没有笛卡儿坐标系的时候,奶牛
们根本没有明确的位置概念。如果一头奶牛问:“妞妞,你现在在哪里啊?”妞妞只能回答说“我
在这里”或者“我在那里”这些模糊的词语。但那头奶牛永远不会知道妞妞的确切位置。而把笛
卡儿坐标系引入到奶牛农场后,所有的一切都变得清晰起来。我们把奶牛农场的中心定义成坐标
原点,而把地理方向中的东、北定义成坐标轴方向。现在,如果奶牛再问:“妞妞,你现在在哪里
啊?”妞妞就可以回答说:“我在东1 米、北3 米的地方。”如图4.5 所示。

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_数学模型_04

4.2.2 三维笛卡儿坐标系

在上面一节中,我们已经了解了二维笛卡儿坐标系。可以看出,二维笛卡儿坐标系实际上是
比较简单的。那么,三维比二维只多了一个维度,是不是也就难了50%而已呢?
不幸的是,答案是否定的。三维笛卡儿坐标系相较于二维来说要复杂许多,但这并不意味着
很难学会它。对人类来说,我们生活的世界就是三维的,因此对于理解更低维度的空间(一维和
二维)是比较容易的。而对于同等维度的一些概念;理解起来难
度就大一些;对于更高维度的空间(如四维空间),理解难度就更
大了。
在三维笛卡儿坐标系中,我们需要定义3 个坐标轴和一个原
点。图4.6 显示了一个三维笛卡儿坐标系。
这3 个坐标轴也被称为是该坐标系的基矢量(basis vector)。
通常情况下,这 3 个坐标轴之间是互相垂直的,且长度为1,这
样的基矢量被称为标准正交基(orthonormal basis),但这并不是
必须的。例如,在一些坐标系中坐标轴之间互相垂直但长度不为
1,这样的基矢量被称为正交基(orthogonal basis)。如非特殊说
明,本书默认情况下使用的坐标轴指的都是标准正交基。
读者:正交这个词是什么意思呢?
我们:正交可以理解成互相垂直的意思。在下面矩阵的内容中,我们还会看到正交矩阵的
概念。
和二维笛卡儿坐标系类似,三维笛卡儿坐标系中的坐标轴方向也不是固定的,即不一定是像
图4.6 中那样的指向。但这种不同导致了两种不同种类的坐标系:左手坐标系(left-handed
coordinate space)和右手坐标系(right-handed coordinate space)。

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_坐标轴_05

4.2.3 左手坐标系和右手坐标系

为什么在三维笛卡儿坐标系中要区分左手坐标系和右手坐标系,而二维中就没有这些烦人的
事情呢?这是因为,在二维笛卡儿坐标系中,x 轴和y 轴的指向虽然可能不同,就如我们在图4.4
中看到的一样。但我们总可以通过一些旋转操作来使它们的坐标轴指向相同。以图4.4 中OpenGL
和DirectX 使用的坐标系为例,为了把右侧的坐标轴指向转换到左侧那样的指向,我们可以首先
对右侧的坐标系顺时针旋转180°,此时它的y 轴指向上,而x 轴指向左。然后,我们再把整个纸
面水平翻转一下,就可以把x 轴翻转到指向右了,此时左右两侧的坐标轴指向就完全相同了。从
这种意义上来说,所有的二维笛卡儿坐标系都是等价的。
但对于三维笛卡儿坐标系,靠这种旋转有时并不能使两个不同朝向的坐标系重合。例如,在
图4.6 中,+z 轴的方向指向纸面的内部,如果有另一个三维笛卡儿坐标系,它的+z 轴是指向纸面
外部,x 轴和y 轴保持不变,那么我们可以通过旋转把这两个坐标轴重合在一起吗?答案是否定
的。我们总可以让其中两个坐标轴的指向重合,但第三个坐标轴的指向总是相反的。
也就是说,三维笛卡儿坐标系并不都是等价的。因此,就出现了两种不同的三维坐标系:左
手坐标系和右手坐标系。如果两个坐标系具有相同的旋向性(handedness),那么我们就可以通过
旋转的方法来让它们的坐标轴指向重合。但是,如果它们具有不同的旋向性(例如坐标系A 属于
左手坐标系,而坐标系B 属于右手坐标系),那么就无法达到重合的目的。
那么,为什么叫左手坐标系和右手坐标系呢?和手有什么关系?这是因为,我们可以利用我
们的双手来判断一个坐标系的旋向性。请读者举起你的左手,用食指和大拇指摆出一个“L”的
手势,并且让你的食指指向上,大拇指指向右。现在,伸出你的中指,不出意外的话它应该指向
你的前方(如果你一定要展示自己骨骼惊奇的话我也没有办法)。恭喜你,你已经得到了一个左手
坐标系了!你的大拇指、食指和中指分别对应了+x、+y 和+z 轴的方向,如图4.7 所示。
同样,读者可以通过右手来得到一个右手坐标系。举起你的右手,这次食指仍然指向上,中
指指向前方,不同的是,大拇指将指向左侧,如图4.8 所示。

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_坐标轴_06

正如我们之前所说,左手坐标系和右手坐标系之间无法通过旋转来同时使它们的3 个坐标轴
指向重合,如果你不信,你现在可以拿自己的双手来试验一下。
另外一个确定是左手还是右手坐标系的方法是,判断前向(forward)的方向。请读者坐直,
向右伸直你的右手,此时右手方向就是x 轴的正向,而你的头顶向上的方向就是y 轴的正向。这

时,如果你的正前方的方向是z 轴的正向,那么你本身所在的坐标系就是一个左手坐标系;如果
你的正前方的方向对应的是z 轴的负向,那么这就是一个右手坐标系。
除了坐标轴朝向不同之外,左手坐标系和右手坐标系对于正向旋转的定义也不同,即在初高
中物理中学到的左手法则(left-hand rule)和右手法则(right-hand rule)。假设现在空间中有一
条直线,还有一个点,我们希望把这个点以该直线为旋转轴旋转某个角度,比如旋转30°。读者
可以拿一支笔当成这个旋转轴,再拿自己的手当成这个需要旋转的点,可以发现,我们有两个旋
转方向可以选择。那么,我们应该往哪个方向旋转呢?这意味着,我们需要在坐标系中定义一个
旋转的正方向。在左手坐标系中,这个旋转正方向是由左手法则定义的,而在右手坐标系中则是
由右手法则定义的。
在左手坐标系中,我们可以这样来应用左手法则:还是举起你的左手,握拳,伸出大拇指让
它指向旋转轴的正方向,那么旋转的正方向就是剩下4 个手指的弯曲方向。在右手坐标系中,使
用右手法则对旋转正方向的判断类似。如图4.9 所示。
从图3.9 中可以看出,在左手坐标系中,旋转正方向是顺时针的,而在右手坐标系中,旋转
正方向是逆时针的。
左右手坐标系之间是可以进行互相转换的。最简单的方法就是把其中一个轴反转,并保持其
他两个轴不变。
对于开发者来说,使用左手坐标系还是右手坐标系都是可以的,它们之间并没有优劣之分。
无论使用哪种坐标系,绝大多数情况下并不会影响底层的数学运算,而只是在映射到视觉上时会
有差别(见练习题2)。这是因为,一个点或者旋转在空间内来说是绝对的。一些较真儿读者可能
会看不惯“绝对”这个词:“你怎么能忽略相对论呢?这世上一切都是相对的!”这些读者请容我
解释。这里所说的绝对是说,在我们所关心的最广阔的空间中,这些值是绝对的。例如我说,把
你的书从桌子的左边移到右边,你不会对这个过程产生什么疑问,此时我们关心的整个空间就是
桌子这个空间,而在这个空间中,书的运动是绝对的。但是,在数学的世界中,我们需要使用一
种数学模型来精确地描述它们,这个模型就是坐标系。一旦有了坐标系,每个点的位置就不再是
绝对的,而是相对于这个坐标系来说的。这种相对关系导致,即便从数学表示上来说两种表示方
式完全一样,但从视觉上来说是不一样的。
我们可以在奶牛农场的例子中体会左手坐标系和右手坐标系的分别。我们假设,妞妞想要到一个
新的地方,因为那里的草很美味。妞妞知道到达这个目标点的“绝对路径”是怎样的,如图4.10 所示。

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_学习Shader数学基础_07
我们可以分别在一个左手坐标系和右手坐标系中描述这样一次运动,即使用数学表达式来描
述它。我们会发现,在不同的坐标系中描述这样同一次运动是不一样的,如图4.11 所示。

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_数学模型_08

在左手坐标系中,3 个坐标轴的朝向如图4.11 左图所示。妞妞首先向x 轴正方向平移1 个单
位,然后再向z 轴负方向移动4 个单位,最后朝旋转的正方向旋转60°。而在右手坐标系中,+z
轴的方向和左手坐标系中刚好相反,因此妞妞首先向x 轴正方向平移1 个单位(与左手坐标系中
的移动一致),然后再向z 轴正方向移动4 个单位(与左手坐标系中的移动相反),最后朝旋转的
负方向旋转60°(与左手坐标系中的旋转相反)。
可以看出,为了达到同样的视觉效果(这里指把妞妞移动到视觉上的同一个位置),左右手坐
标系在z 轴上的移动以及旋转方向是不同的。如果使用相同的数学运算(指均向z 轴某方向移动
或均朝旋转正方向旋转等),那么得到的视觉效果就是不一样的。因此,如果我们需要从左手坐标
系迁移到右手坐标系,并且保持视觉上的不变,就需要进行一些转换。读者可以参见本章最后的
扩展阅读部分。

4.2.4 Unity 使用的坐标系

对于一个需要可视化虚拟的三维世界的应用(如Unity)来说,它的设计者就要进行一个选
择。对于模型空间和世界空间(在4.6 节中会具体讲解这两个空间是什么),Unity 使用的是左手
坐标系。这可以从Scene 视图的坐标轴显示看出来,如图4.12 所示。这意味着,在模型空间中,
一个物体的右侧(right)、上侧(up)和前侧(forward)分别对应了x 轴、y 轴和z 轴的正方向。

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_数学模型_09

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

Unity Shader入门精要--第4 章 学习Shader 所需的数学基础_学习Shader数学基础_10

关于Unity 中使用的坐标系的旋向性,我们会在4.5.9 节中详细地讲解。

参考

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