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

Unity系列文章目录

前言

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

4.1 背景:农场游戏

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

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)的位置上画一个点。”那么相信读者肯定知道这个位置在哪里。
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

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)。

在这里插入图片描述

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 所示。

在这里插入图片描述

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

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

在这里插入图片描述
我们可以分别在一个左手坐标系和右手坐标系中描述这样一次运动,即使用数学表达式来描
述它。我们会发现,在不同的坐标系中描述这样同一次运动是不一样的,如图4.11 所示。

在这里插入图片描述

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

在这里插入图片描述

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

参考

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity Shader是一种用于渲染图形的程序,它可以控制对象的表面颜色、纹理、透明度、反射等属性,从而实现特殊的视觉效果。对于游戏开发者来说,掌握Shader编写技巧是非常重要的。 以下是关于Unity Shader入门精要: 1. ShaderLab语言 ShaderLab是Unity中用于编写Shader的语言,它是一种基于标记的语言,类似于HTML。ShaderLab可以用于定义Shader的属性、子着色器、渲染状态等信息。 2. CG语言 CG语言是Unity中用于编写Shader的主要语言,它是一种类似于C语言的语言,可以进行数学运算、向量计算、流程控制等操作。CG语言可以在ShaderLab中嵌入,用于实现Shader的具体逻辑。 3. Unity的渲染管线 Unity的渲染管线包括顶点着色器、片元着色器、几何着色器等组件,每个组件都有不同的作用。顶点着色器用于对对象的顶点进行变换,片元着色器用于计算每个像素的颜色,几何着色器用于处理几何图形的变形和细节等。 4. 模板和纹理 在Shader中,我们可以使用纹理来给对象添加图案或者贴图,也可以使用模板来控制对象的透明度、反射等属性。纹理可以通过内置函数tex2D()来获取,模板可以通过内置函数clip()来实现裁剪。 5. Shader的实现 Shader的实现要注意以下几点: - 在ShaderLab中定义Shader的属性、子着色器、渲染状态等信息。 - 在CG语言中实现Shader的具体逻辑,包括顶点着色器、片元着色器等内容。 - 使用纹理和模板来实现特定的视觉效果。 - 在对象上应用Shader,通过调整Shader的属性来达到不同的效果。 以上是关于Unity Shader入门精要,希望对你有所帮助。如果你想更深入地了解Shader的编写技巧,可以参考官方文档或者相关教程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值