Section03 Shader数学基础

最后更新于 2022-11-29 407 次阅读


笛卡尔坐标系

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

二维笛卡尔坐标系

包含了两个部分信息:

  • 一个特殊的位置,即原点,它是整个坐标系的中心
  • 两条过原点的互相垂直的矢量,即x轴和y轴。这些坐标轴也被称为是该坐标系的基矢量。

在屏幕映射时,OpenGL和DirectX使用了不同方向的二维笛卡尔坐标系

三维笛卡尔坐标系

在三维笛卡尔坐标系中我们需要定义3个坐标轴和一个原点,这三个坐标轴也被称为是该坐标系的基矢量

通常情况下,这3个坐标轴之间是互相垂直的,且长度为1,这样的基矢量被称为标准正交基

和二维笛卡儿坐标系类似,三维笛卡儿坐标系中的坐标轴方向也不是固定的,这种不同导致了两种不同种类的坐标系 :左手坐标系 (left-handed coordinate space) 右手坐标系 (right-handed coordinate space)

左手坐标系和右手坐标系

从这种意义上来说,所有的二维笛卡儿坐标系都是等价的。但对于三维笛卡儿坐标系,靠这种旋转有时并不能使两个不同朝向的坐标系重合。

左手坐标系

确定坐标系

确定是左手还是右手坐标系的方法是,判断前向 (forward) 的方向。请读者坐直,向右伸直你的右手,此时右手方向就是 轴的正向,而你的头顶向上的方向就是 轴的正向 。这时,如果你的正前方的方向是 轴的正向,那么你本身所在的坐标系就是一个左手坐标系。如果你的正前方的方向对应的是 轴的负向,那么这就是一个右手坐标系。

旋转法则

在左手坐标系中,这个旋转正方向是由左手法则定义的,而在右手坐标系中则是由右手法则定义的。

Unity所使用的的坐标系

对千模型空间和世界空间,Unity 使用的是左手坐标系。

对于观察空间来说,Unity 使用的是右手坐标系。观察空间 ,通俗来讲就是以摄像机为原点的坐标系。在这个坐标系中,摄像机的前向是z轴的负方向。

点和矢量

点(point) 是n维空间(游戏中主要使用二维和三维空间)中的一个位置。它没有大小、宽度这类概念。

矢量,也被称为向量。矢量存在的意义更多是为了和标量区分开来。矢量是指n维空间中一种包含了方向的有向线段,我们通常讲到的速度就是一种典型的矢量。

  • 矢量的模指的是这个矢量的长度
  • 矢量的方向则描述了这个矢量在空间中的指向

通常,矢量被用于表示相对于某个点的偏移,也就是说它是一个相对量。

矢量运算

  • 一个矢量可以被一个非零的标量除
  • 可以对两个矢量进行相加或相减,其结果是一个相同维度的新矢量。在图形学中通常用于描述位置偏移
  • 矢量的模是一个标量
  • 单位矢量是指那些模为1 的矢量。也被称为被归一化的矢量。法线法相也被称为法矢量

矢量之间也可以进行乘法,但是和标量之间的乘法有很大不同。矢量的乘法有两种最常用的
种类:点积 (dot product, 也被称为内积, inner product) 和叉积 (cross product, 也被称为外
积, outer product)。

矢量的点积

  • 公式一:

a·b=(ax, ay, az)·(bx, by, bz)= axbx+ayby+azbz

矢量的点积满足交换律,即 a·b = b·a
点积的几何意义很重要,因为点积几乎应用到了图形学的各个方面。其中一个几何意义就是
投影 (projection)
。投影的值可能是负数。

也是就是说 点积的符号可以让我们知道两个矢量的方向关系。

点积的性质

  • 点积可结合标量乘法。也就是说,对点积中其中一个矢量进行缩放的结果 ,相当于对最后的点积结果进行缩放。
  • 点积可结合矢量加法和减法
  • 一个矢量和本身进行点积的结果,是该矢量的模的平方。这意味着,我们可以直接利用点积来求矢量的模, 而不需要使用模的计算公式

  • 公式二

a · b = |a||b|cosθ

两个单位矢量的点积等于它们之间夹角的余弦值

矢量的叉积

与点积不同的是, 矢量叉积的结果仍是一个矢量, 而非标量。

ab = (ax, ay, az)(bx, by, bz) = (aybz - azby, azbx - axbz, axby - aybx)

需要注意的是,叉积不满足交换律 a x b != b x a。实际上,叉积是满足反交换律的,即a x b = -(b x a)。而且叉积也不满足结合律,即 (a x b) x c != a x (b x c)。

对两个矢量进行叉积的结果会得到 个同时垂直于这两个矢量的新矢量。

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

  • 在渲染中我们时常会需要判断一个三角面片是正面还是背面, 这可以通过判断三角形的3个顶点在当前空间中是顺时针还是逆时针排列来得到。

矩阵

在三维数学中, 我们通常会使用矩阵来进行变换。 一个矩阵可以把一个矢量从一个坐标空间转换到另一个坐标空间。在第2章 渲染流水线中, 我们就看到了很多坐标变换, 例如在顶点着色器中我们需要把顶点坐标从模型空间变换到齐次裁剪坐标系中。

与矢量联系

实际上,矢量可以看成是 n x 列矩阵 (column matrix) 或 l x n 行矩阵 (row matrix), 其中 对应了矢量的维度。

矩阵乘法

  • 矩阵乘法并不满足交换律
  • 矩阵乘法满足结合律

特殊矩阵

方块矩阵

方块矩阵 (square matrix), 简称方阵,是指那些行和列数目相等的矩阵 维渲染里,最常使用的就是 3X3 4X4 的方阵。

单位矩阵

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

转置矩阵

转置矩阵 (transposed matrix) 实际是对原矩阵的一种运算,即转置运算 给定 rXc的矩阵M,它可以表示成Mt。,原矩阵的第i行变成了第j列,而第j列变成了第i行。

  • 矩阵转置的转置等于原矩阵
  • 矩阵串接的转置 等于反向串接各个矩阵的转置

逆矩阵

不是所有的矩阵都有逆矩阵,第一个前提就是,该矩阵必须是一个方阵。逆矩阵最重要的性质就是 如果我们把M和M-1相乘,那么它的结果将会是一个单位矩阵。

如果一个矩阵有对应的逆矩阵,我们就说这个矩阵是可逆的 (invertible) 或者说是非奇异的 (nonsingular)相反的,如果 个矩阵没有对应的逆矩阵,我们就说它是不可逆的 (noninvertible) 或者说是奇异的 (singular)。

如果 个矩阵的行列式 (determinant)不为0那么它就是可逆的。

  • 逆矩阵的逆矩阵是原矩阵本身
  • 单位矩阵的逆矩阵是它本身
  • 转置矩阵的逆矩阵是逆矩阵的转置
  • 矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵

正交矩阵

正交是矩阵的一种属性。如果一个方阵和它的转置矩阵的乘积是单位矩阵的话,我们就说这个矩阵是正交的 (orthogonal) 。反过来也是成立的。

如果一个矩阵是正交的,那么它的转置矩阵和逆矩阵是一样的

为在三维变换中我们经常会需要使用逆矩阵来求解反向的变换。而逆矩阵的求解往往计算批很大,但转置矩阵却非常容易求解 我们只需要把矩阵翻转一下就可以了。

因此,如果这些基矢量是一组标准正交基的话(例如只存在旋转变换,那么我们就可以直接使用转置矩阵来求得该变换的逆变换。

矩阵几何意义:变换

变换

变换(transform), 指的是我们把一些数据, 如点、 方向矢批甚至是颜色等, 通过某种方式进行转换的过程。

线性变换指的是那些可以保留矢量加和标量乘的变换。

  • 缩放(scale)就是一种线性变换
  • 旋转(rotation)也是一种线性变换
  • 线性变换除了包括旋转和缩放外,还包括错切(shear)、镜像(mirroring, 也被称为reflection)、正交投影(orthographic projection)等

仿射变换

我们不能用 3X3 的矩阵来表示一个平移变换,所以有了仿射变换

仿射变换就是合并线性变换和平移变换的变换类型。仿射变换可以使用一个 4x4 的矩阵(齐次坐标)来表示,为此,我们需要把矢量扩展到四维空间下,这就是齐次坐标空间 (homogeneous space)

坐标空间

我们需要在不同的情况下使用不同的坐标空间 因为一些概念只有在特定的坐标空间下才有意义 才更容易理解。这也是为什么在渲染中我们要使用这么多坐标空间。

坐标空间的变换

当给定一个坐标空间以及其中一点 时,我们是如何知道该点的位置的呢?我们可以通过 个步骤来确定它的位置:

  1. 从坐标空间的原点开始;
  2. 向x轴方向移动a个单位。
  3. 向y轴方向移动b个单位。
  4. 向z轴方向移动c个单位。

对方向矢量的坐标空间转换

矢量是没有位置的,因此坐标空间的原点变换是可以忽略的。也就是说,我们仅仅平移坐标系的原点是不会对矢量造成任何影响的。那么,对矢址的坐标空间变换就可以使用 3X3 的矩阵来表示,因为我们不需要表示平移变换。

在Shader中经常会看到截取变换矩阵的前3行前3列来对法线方向、光照方向来进行空间变换,这正是原因所在

顶点的坐标空间变换过程

在渲染流水线中,一个顶点要经过多个坐标空间的变换才能最终被画在屏器上 。一个顶点最开始是在模型空间中定义的,最后它将会变换到屏幕空间中,得到真屏幕像素坐标。

模型空间

模型空间 (model space), 如它的名字所暗示的那样,是和某个模型或者说是对象有关的。有时模型空间也被称为 对象空间 (1)bject space) 局部空间 (local space) 。

在模型空间中,我们经常使用一些方向概念,例如“前 (forward)" "后(back )" "左 (left )"“右 (right)" "上 (up)""下(down )" 。

世界空间

世界空间 (world space) 个特殊的坐标系,因为它建立了我 所关 的最大的空间。

世界空间可以被用于描述绝对位置。

在Unity中,世界空间同样使用了左手坐标系。但它的x轴,y轴,z轴是固定不变的。

顶点变换的第一步,就是将顶点坐标从模型空间变换到世界空间中 这个变换通常叫模型变换

观察空间

观察空间 (view space) 也被称为摄像机空间 (camera space) 。观察空间可以认为是模型间的一个特例-在所有的模型中有一个非常特殊的模型,即摄像机(虽然通常来说摄像机本身是不可见的),它的模型空间值得我们单独拿出来讨论 ,也就是观察空间。

摄像机决定了我们渲染游戏所使用的视角。

Unity 在模型空间和世界空间中选用的都是左手坐标系,而在观察空间中使用的是右手坐标系。这是符合 OpenGL 传统的, 在这样的观察空间中 ,摄像机的 正前方指向的是-z 方向。

观察空间和屏幕空间是不同 。观察空间是一个三维空间 ,而屏幕空间是一个二维空间。从观察空间到屏幕空间的转换需要经过一个操作,那就投影 (projection)

顶点变换的第二步 ,就是将顶点坐标从世界空间变换到观察空间中。这个变换通常叫做观察
变换 (view transform)

裁剪空间

项点接下来要从观察空间转换到裁剪空间 (clip space, 也被称为齐次裁剪空间) 这个用
于变换的矩阵叫做裁剪矩阵 (clip matrix), 也被称为投影矩阵 (projection matrix)

裁剪空间的目标是能够方便地对渲染图元进行裁剪 完全位千这块空间内部的图元将会被保
留,完全位于这块空间外部的图元将会被剔除,而与这块空间边界相交的图元就会被裁剪。

视锥体(view frustum) 决定了裁剪空间。视锥体指的是空间中的一块区域,这块区域决定了摄像机可以看到的空间。视锥体由六个平面包围而成,这些平面也被称为裁剪平面 (clip planes) 。视锥体有两种类型,这涉及两种投影类型:

  • 正交投影 (orthographic projection)
  • 透视投影 (perspective projection)

透视投影模拟了人眼看世界的方式,而正交投影则完全保留了物体的距离和角度。因此,在追求真实感的 游戏中我们往往会使用透视投影,而在 游戏或渲染小地图等其他HUD 元素时,我们会使用正交投影。

在视锥体的 块裁剪平面中 有两块裁剪平面比较特殊,它们分别被称为近剪裁平面(near clipplane) 远剪裁平面 (far clip plane) 。它们决定了摄像机可以看到的深度范围。

有一种更加通用 、方便和整洁的方式来进行裁剪的工作,这种方式就是通过一个投影矩阵把顶点转换到一个裁剪空间中。投影矩阵有两个目的:

  • 为投影做准备。但是它并没有进行真正的投影工作,而是在为投影做准备 。真正 的投影发生在后面的齐次除法(homogeneous division) 过程中 而经过投影矩阵的变换后,顶点的w分量将会具有特殊的意义。
  • 其次是对x y z分量进行缩放

透视投影

视锥体的意义在于定义了场景中的一块三维空间。所有位于这块空间内的物体将会被渲染,否则就会被剔除或裁剪。

这个投影矩阵本质就是对 分量进行了不同程度的缩放(当然z分量还做了一个平移),缩放的目的是为了方便裁剪。

正交投影

正交投影的视锥体是一个长方体 因此计算上相比透视投影来说更加简单。

和透视投影不同的是,使用正交投影的投影矩阵对顶点进行变换后,其w分量仍然为1。本质是因为投影矩阵最后一行的不同,透视投影的投影矩阵的最后一行是[0 0~1 0],正交投影的投影矩阵的最后 行是[0 0 0 1] 。这样的选择是有原因的,是为了为齐次除法做准备。

屏幕空间

当完成了所有的裁剪工作后,就需要进行真正的投影了,也就是说,我们需要把视锥体投影到屏幕空间(screen space) 中。经过这一步变换,我们会得到真正的像素位置,而不是虚拟的三维坐标。

屏幕空间是一个二维空间,因此,我们必须把顶点从裁剪空间投影到屏幕空间中,来生成对
应的20坐标。这个过程可以理解成有两个步骤:

  1. 首先,我们需要进行标准齐次除法(homogeneous division), 也被称为透视除法(perspective division)。经过这一步,我们可以把坐标从齐次栽剪坐标空间转换到NDC中。
    • 按照OpenGL的传统,这个立方体的x、 y、 z分址的范围都是[-1, l]。但 在DirectX这样的API中,z分噩的范围会是[0,1]。而Unity选择了 OpenGL这样的齐次裁剪空间。
    • 而对于正交投影来说,它的裁剪空间实际已经是一个立方体了,而且由于经过正交投影矩阵变换后的顶点的w分量是 1, 因此齐次除法并不会对顶点的x y z坐标产生影响
  2. 经过齐次除法后,透视投影和正交投影的视锥体都变换到一个相同的立方体内。现在,我们可以根据变换后的 坐标来映射输出窗口的对应像素坐标。

顶点着色器的最基本的任务就是把顶点坐标从模型空间转换到裁剪空间中。

法线变换

法线

法线变换是一种特殊的变换

法线 (normal), 也被称为法矢量 (normal vector)

法线是需要我们特殊处理的一种方向矢量。在游戏中,模型的一个顶点往往会携带额外的信息,而顶点法线就是其中 种信息。当我们变换一个模型的时候,不仅需要变换它的顶点,还需要变换顶点法线,以便在后续处理(如片元着色器)中计算光照等。

一般来说,点和绝大部分方向矢量都可以使用同一个 4X4 3X3 的变换矩阵 MA 把其从坐标空间 变换到坐标空间 。但在变换法线的时候,如果使用同一个变换矩阵,可能就无法确保维持法线的垂直性。

切线

切线(tangent), 也被称为切矢量 (ta ngent vector) 。与法线类似,切线往往也是模型顶点携带的一种信
息。它通常与纹理空间对齐,而且与法线方向垂直。由于切线是由两个顶点之间的差值计算得到的,因此我们可以直接使用用于变换顶点的变换矩阵来变换切线。

进行非统一缩放时,如果使用和变换顶点相同的变换矩阵来变换法线,就会得到错误的结果,即变换后的法线方向与平面不再垂直