1. 笛卡尔坐标系

在游戏中,我们使用的数学大部分都是为了计算位置、距离和角度等变量。而这些就算大部分是在笛卡尔坐标系下进行的。

1.1 二维笛卡尔坐标系

一个二维笛卡尔坐标系包含了两个部分的信息
1.一个特殊的位置,即原点,他是整个坐标系的中心
2.两条过原点的互相垂直的矢量,即x轴和y轴。这些坐标轴也被称为是该坐标系的基矢量。

虽然在图中x轴和y轴分别是水平方向的和垂直方向的。但这并不是必须的。而且虽然图中的x轴指向右、y轴指向上,但这也不是必须的。如下图所示

1.2 三维笛卡尔坐标系

在三维笛卡尔坐标系中,我们需要定义三个坐标轴和一个原点。下图展示了一个三维笛卡尔坐标系。

这三个坐标轴也被称为是该坐标系的基矢量(basis vector)。通常情况下,这三个坐标轴之间是互相垂直的,且长度为1,这样的基矢量被称为标准正交基(orthonormal basis),但这并不是必须的。例如,在一些坐标系中坐标轴之间相互垂直但长度不为1,这样的基矢量被称为正交基(orthogonal basis)。
与二维笛卡尔坐标系类似,三维笛卡尔坐标系中的坐标轴方向也不是固定的。这种不同导致了两种不同类型的坐标系:左手坐标系(left-handed coordinate space)和右手坐标系(right-handed coordinate space)


大拇指、食指、中指分别对应+x、+y和+z轴方向。
一个判断左手还是右手坐标系的方法是,判断前向(Forward)的方向。请读者坐直,向右伸直你的右手,此时右手就是x轴的正方向,而你头顶向上的方向就是y轴的正方向,那么你本身所在的坐标系就是一个左手坐标系;如果你的正前方方向是z轴的负向。那么这就是一个右手坐标系。
在左手坐标系中,旋转的正方向是顺时针的,而在右手坐标系中,旋转的正方向是逆时针的。

1.3 Unity使用的坐标系

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

2. 点和矢量

点(point)是n维空间(游戏中主要用二维和三维空间)中的一个位置,它没有大小、宽度这类概念。在笛卡尔坐标系中。我们可以用两个或3个实数表示一个点的坐标。
矢量(vector,也被称为向量)。在数学家看来矢量就是一串数字。你可能要问点的表示不也是一串数字吗?没错,但矢量存在的意义更多是为了和标量(scalar)区分开来。通常来将,矢量是n维空间中一种包含了模(magnitude)和方向(direction)的有向线段。
具体来讲:
1.矢量的模指的是这个矢量的长度。一个矢量的长度可以是任意非负数
2.矢量的方向描述了这个矢量在空间中的指向。
从矢量的定义来看,它只有方向和模两个属性,并没有位置信息。通常矢量被用于表示相对于某个点的偏移(displacement),也就是说它是一个相对量。只要矢量的模和方向保持不变,无论在哪里,都是同一个矢量。

2.1 矢量的点积

点积(dot product ,也被称为內积,inner product)。
点积的几何意义很重要,因为点积几乎用到了图形学的各个方面,其中一个几何意义就是投影(projection)。假设有一个单位向量a和另一个不限长的矢量b。现在我们希望得到b在平行于a的一条直线上的投影。那么我们可以使用点积a·b来得到ba方向上的有符号的投影。
那么投影到底是什么意思呢?这里给出一个通俗解释,我们可以认为现在有一个光源,它发出的光是垂直于a方向的,那么ba方向上的投影就是ba方向上的影子,如图所示。
需要注意的是,投影的值可能是负数。投影结果的正负号与a和b的方向有关:当它们的反向相反(夹角大于90度时),结果小于0;当它们的方向互相垂直(夹角为90度时),结果等于0;当它们的方向相同(夹角小于90度),结果大于0,下图给出了这三种情况的图示。


那么如果a不是一个单位矢量会如何呢?这很容易想到,任意两个矢量的点积a·b等同于ba方向上的投影值,再乘以a的长度。

2.2 矢量的叉积

叉积(cross product)也被称为外积(outer product)。与点积不同的是,矢量叉积的结果仍是一个矢量,而非标量。
矢量叉积的规律图:

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

3. 矩阵的几何意义:变换

3.1 什么是变换

变换(transform),是指把我们的一些数据,如点、方向矢量甚至是颜色等,通过某种方式进行转换的过程。
我们先来看一个非常常见的变换类型——线性变换(Linear transform)。线性变换指的是那些可以保留矢量加和标量乘的变换,用数学公式来表示这两个条件就是:
f(x)+f(y)=f(x+y)
kf(x)=f(kx)
上面的例子看起来很抽象。缩放(scale)就是一种线性变换。例如f(x)= 2x,可以表示一个大小为2的统一缩放,即经过变换后矢量x的模被放大2倍。可以发现f(x)= 2x是满足上面2个条件的。同样,旋转(rotation)也是一种线性变换。对于线性变换来说,如果我们需要对一个三维矢量进行变换,那么仅仅使用3×3矩阵就可以表示所有的线性变换。
线性变换除了旋转和缩放外,还包括错切(shear)、镜像(mirroring,也被称为reflection)、正交投影(orthographic projection)等。
但是,仅有线性变换是不够的。我们来考虑平移交换,例如f(x)= x+(1,2,3)。这个变换就不是一个线性变换,它既不满足标量乘法,也不满足矢量加法,如果我们令x=(1,1,1)那么:
f(x)+f(x)=(4,6,8)
f(x)+f(x)= (3,4,5)
可见,两个运算得到的结果是不一样的。因此我们不能用一个3×3矩阵来表示一个平移变换。这不是我们所希望看到的,毕竟平移变换是非常常见的一种变换。
这样,就有了仿射变换(affine transform)。仿射变换就是合并线性变换和平移变换的变换类型。仿射变换可以使用一个4×4矩阵来表示,为此我们需要把矢量扩展到思维空间下,这就是其次坐标空间(homogeneous space)。
下表给出了图形学中常见的变换矩阵名称及他们的特性。(N表示不满足该特性,Y表示满足该特性)

3.2 齐次坐标

我们知道,由于3×3矩阵不能表示平移操作,因此我们把其扩展到了4×4矩阵。为此,我们还需要把原来的三维矢量转化为四维矢量,也就是我们所说的齐次坐标(homogeneous coordinate)。
如上所说,齐次坐标是一个四维矢量。那么我们如何把三维矢量转化为齐次坐标呢?对于一个点,从三维坐标转换为齐次坐标是把其w分量设置为1,而对于方向矢量来说,需要把其w分量设置为0。这样的设置会导致,当用一个4×4矩阵对一个点进行变换时,平移、旋转、缩放都会施加于该点。但是如果是用于变换一个方向矢量,平移的效果就会被忽略。

3.3 分解基础变换矩阵

我们已经知道,可以使用一个4×4的矩阵来表示平移、旋转、缩放。我们把表示平移、纯旋转和纯缩放的变换矩阵叫做基础变换矩阵。这些矩阵有一些共同点,我们把一个基础变换矩阵分解成4个组成部分。


其中左上角M用于表示旋转和缩放,t用于表示平移,0是0矩阵,右下角的元素就是标量1。
下面,我们具体来学习如何用这样一个矩阵来表示平移、旋转和缩放。

3.4 平移矩阵

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


从结果来看我们很容易看出为什么这个矩阵有平移效果:点的x、y、z分量分别增加了一个位置偏移。在3D可视化的效果是,把x、y、z在空间中平移(tx,ty,tz)个单位。
有趣的是,如果我们对一个方向矢量进行平移变换,结果如下:


可以发现,平移变换不会对方向矢量产生任何影响。这点很容易理解,我们在学习矢量的时候就说过了,矢量没有位置属性,也就是说它可以位于空间中的任意一点,因此位置的改变(即平移)不应该对矢量产生影响。
那么,现在我们应该明白当给定一个平移操作时如何构建一个平移矩阵:基础变换矩阵中的t矢量对应了平移矢量,左上角的M矩阵既是单位矩阵为单位矩阵I。
平移矩阵的逆矩阵就是反向平移得到的矩阵,即

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

3.5 缩放矩阵

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

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

如果缩放系数kx=ky=kz,那么我们把这样的缩放称为统一缩放(uniform scale),否则称为非统一缩放(nonuniform scale)。从外观上看,统一缩放是扩大整个模型,而非统一缩放会拉伸或挤压模型。更重要的是。统一缩放不会改变角度和比例信息,而非统一缩放会改变与模型相关的角度和比例。例如在对法线进行变换时,如果存在非统一缩放,直接使用用于变换顶点的变换矩阵的话,就会得到错误的结果。
缩放矩阵的逆矩阵是使用原缩放系数的倒数来对点或方向矢量进行缩放,即

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

3.6 旋转矩阵

旋转是三种常见的变换矩阵中最复杂的一种。我们知道,旋转操作需要指定一个旋转轴,这个旋转轴不一定是空间中的坐标轴,但本节所讲的旋转就是指绕着空间的x轴、y轴或z轴进行旋转。
如果我们要把点绕着x轴旋转θ度可以使用下面的矩阵:


绕y轴旋转也是类似的

最后,绕Z轴的旋转:

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

3.7 复合变换

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

由于上面我们使用的是列矩阵,因此阅读顺序是从右往左,即先进行缩放变换,在进行旋转变换,最后进行平移变换。需要注意的是,变幻的结果是依赖于变换顺序的,由于矩阵乘法不满足交换律,因此矩阵的乘法顺序很重要。也就是说,不同的变换顺序得到的结果可能是不一样的。
在绝大多数情况下,我们约定的变换顺序就是先缩放,再旋转,最后在平移。
除了需要注意不同类型的变换顺序外,我们有时还需要小心旋转的变换顺序。在上面我们给出了分别绕x轴、y轴和z轴的变换矩阵,一个问题是,如果我们需要同时绕着3个轴进行旋转,是先绕x轴、再绕y轴最后绕z轴旋转还是按其他的旋转顺序呢?
当我们直接给出(θx,θy,θz)这样的旋转角度时,需要定义一个旋转顺序。在unity中,这个旋转顺序是zxy,这在旋转的API文档中都有说明。这意味着,当给定(θx,θy,θz)这样的旋转角度时,得到的组合旋转变换矩阵是:

一些读者会有疑问,上面的公式书写是不是反了?不是说列矩阵要从右往左读吗?这样一来顺序不是颠倒了吗?实际上,有一个重要的东西我们没有说明白,那就是旋转时所用的坐标系。给定一个旋转顺序(例如这里的zxy)以及它们对应的旋转角度(θx,θy,θz),有两种坐标系可以选择。
(1)绕坐标系E下的z轴旋转θz,绕坐标系E下的y轴旋转θy,绕坐标系E下的x轴旋转θx,即进行一次旋转时不一起旋转当前坐标系。
(2)绕坐标系E下的z轴旋转θz,绕坐标系E下的z轴旋转θz后的新坐标系E‘下的y轴旋转θy,在坐标系E’下绕y轴旋转θy后的新坐标系E”的x轴旋转θx,即在旋转时,把坐标系一起转动。
很容易知道这两种选择的结果是不一样的。但如果把他们的旋转顺序颠倒一下,它们得到的结果就会是一样的。说明白点,在第一种情况下,按zxy顺序旋转和在第二种情况下,按yxz顺序旋转是一样的。而在unity文档中说明的旋转顺序指的是在第一种情况下旋转。

第三章 学习Shader所需的数学基础(1)的更多相关文章

  1. 第三章 学习Shader所需的数学基础(2)

    目录 1.坐标空间 1.2 坐标空间的变换 @ 1.坐标空间 我们在以前渲染流水线中就接触了坐标空间的变换.例如,在学习顶点着色器流水线阶段时,我们说过,顶点着色器的最基本功能就是把模型的顶点坐标从模 ...

  2. 第三章 学习Shader所需的数学基础(5)

    1. Unity Shader的内置变量(数学篇) 使用Unity写shader的一个好处在于,它提供了很多内置参数,这使得我们不在需要自己手动算一些值.本文给出Unity内置的用于空间变换和摄像机以 ...

  3. 第三章 学习Shader所需的数学基础(3)

    @[TOC] 1. 顶点的坐标空间变换过程 我们知道,在渲染流水线中,一个顶点要经过多个坐标空间的变换才能最终被画在屏幕上.一个顶点最开始是在模型空间中定义的,它最后会被变换到屏幕空间中,得到真正的屏 ...

  4. 第三章 学习Shader所需的数学基础(4)

    法线变换 法线(normal),也被称为法矢量(normal vector).在以前我们已经讲过如何使用变换矩阵来变换一个顶点或方向矢量,但法线是需要我们特殊处理的一种方向矢量.在游戏中,模型的顶点往 ...

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

    摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐 ...

  6. 学习Shader所需的数学基础(坐标系,点和矢量)

    数学对于计算机图形学的重要性是不言而喻的.在学习Shader之前,首先就要打好数学基础,好在入门Unity Shader所需的数学知识都是线性代数中很基础的的内容.按部就班的来,第一篇文章记录总结的是 ...

  7. 《ORACLE数据库管理与开发》第三章学习之常用函数记录

    <ORACLE数据库管理与开发>第三章学习之常用函数记录 注:文章中的*代表所要操作的列名 1.lower(*)/upper(*),将此列下的值转为小写/大写 2.initcap(*):把 ...

  8. 《Linux内核设计与实现》第三章学习笔记

    第三章  进程管理 姓名:王玮怡  学号:20135116 一.进程 1.进程的含义 进程是处于执行期的程序以及相关资源的总称,程序本身并不是进程,实际上就是正在执行的代码的实时结果.Linux内核通 ...

  9. 20135202闫佳歆--week6 课本第三章学习笔记

    第三章 进程管理 一.进程 1.进程 进程就是处于执行期的程序. 进程就是正在执行的程序代码的实时结果. 进程是处于执行期的程序以及相关的资源的总称. 进程包括代码段和其他资源. 2.线程 执行线程, ...

随机推荐

  1. [2018-06-28] 创建第一个django项目

    1.创建一个名称为tmpl的项目 django-admin.py startproject tmpl 2.进入刚刚创建的tmpl目录 cd tmpl 3.创建一个名称为learn的应用 python ...

  2. 【工利其器】Android Lint篇——为Android量身定做的静态代码审查工具

    前言 我们在进行代码优化的时候,往往是通过开发者的经验来判断哪些代码可能存在潜在问题,哪些资源的使用不合规范等.实际上Android SDK提供了一款功能非常强大的工具,来帮助开发者自动检测代码的质量 ...

  3. scss新手使用指南

    还在用死的css写样式吗?那可太麻烦了,各种长串选择器不说,还有各种继承权重有时候还有可能不生效 我的小程序项目也结束了,是时候总结一下scss语法了,毕竟用起来更加方便而且还能精简一点代码,好处多多 ...

  4. 网页文件打包成.exe可执行文件

    网页文件不止可以通过浏览器打开,也可以通过打包程序打包成.exe桌面可执行程序,这样写的网页文件就可以像桌面应用一样打开了.方法如下: 工具:NW.js.Enigma Virtual Box.资源编辑 ...

  5. Spring Boot 配置 - Consul 配置中心

    ▶ Spring Boot 依赖与配置 Maven 依赖 <dependencyManagement> <dependencies> <dependency> &l ...

  6. Appium+python自动化(三十九)-Appium自动化测试框架综合实践 - 代码实现(超详解)

    简介 经过一段时间的准备,完善的差不多了,继续分享有关Appium自动化测试框架综合实践.想必小伙伴们有点等不及了吧! driver配置封装 kyb_caps.yaml 配置表 参考代码 platfo ...

  7. access,trunk,hybrid端口分析

    1.access 接收:当数据没有tag时打上pvidtag进入,若有则看是否与pvid相等,相等则接收,不想等则丢弃. 转发:看tag是否等于pvid,若等则去tag发送,否则不处理. 2.trun ...

  8. 算法编程题积累(4)——腾讯笔试"有趣的数字“问题

    本题基本思路:先对原序列进行排序,再根据不同情况采用不同算法. 首先差最大的对数最好求:用最小的数的个数 × 最大的数的个数即可. 接着求差最小的对数: 1.当序列中无重复关键字时:可知最小差必然产生 ...

  9. python线程threading.Timer源码解读

    threading.Timer的作用 官方给的定义是: """Call a function after a specified number of seconds: t ...

  10. LeetCode18. 四数之和

    LeetCode18. 四数之和 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值 ...