图形学—线性代数基础
声明:本文是从图形学的角度而非从数学课的角度介绍涉及到的线性代数概念,并且默认读者有一定的线性代数基础(建议观看 3Blue1Brown 的视频)。
变换(transform)
在三维渲染中,矩阵是可以可视化的,可视化的结果就是变换。在游戏开发中,变换指的是我们把点,方向,颜色通过某种方式进行映射的过程。接下来本文将介绍这些变换的含义和使用方式,以及具体如何用矩阵来表示这些变换。
线性变换(linear transform)
线性变换指的是可以保留矢量加(加法性)和标量乘(齐次性)的一种映射,规范的来说,线性变换的数学定义如下:
加法性:
齐次性:
在三维渲染中,线性变换
其中,
常见的线性变换有旋转,缩放,错切,镜像,正交投影。下面优先介绍最基础的两种。
旋转变换
缩放变换
当
接触过游戏开发的读者可能会对为什么没有提到平移变换抱有疑问,我们可以考虑一下平移变换数学表示:
它不满足加法性:
也不满足齐次性:
很明显平移并不是一个线性变换,这也意味着我们不能用3x3的矩阵来表示它。不过我们自然会期待有一种统一的方式来表示所有常用的变换,由此我们引入仿射变换。
仿射变换(affine transform)
仿射变换就是合并线性变换和平移变换的变换类型。为了能确保平移变换正常生效,仿射变换需要使用一个4x4的矩阵来表示,所以我们需要将矢量扩展到四维空间,由此我们引入了齐次坐标空间。
齐次坐标(homogeneous coordinate)
齐次坐标有四个维度,分别是
将三维矢量转换为齐次坐标空间的四维矢量的方式如下:
- 对于位置矢量,将其w分量设置为1。
- 对于方向矢量,将其w分量设置为0。
接下来我们介绍这样设置的原因。
分解基础变换矩阵
在变换位置矢量和方向矢量时的关键差异在于是否要应用平移变换部分,位置矢量的变换需要应用平移,但方向矢量需要忽略平移。
下面是一个可以表示平移,旋转和缩放的4x4仿射变换矩阵:
左上角矩阵
当仿射变换作用于位置矢量
当仿射变换作用于方向矢量
很自然的注意到当
1. 平移矩阵
其中,
2. 缩放矩阵
其中,
3. 旋转矩阵
以绕
复合变换
变换的复合是通过矩阵乘法实现的。我们都知道矩阵乘法不满足交换律,所以在复合变换的时候矩阵乘法顺序也很重要。绝大数情况下,约定的变换顺序为先缩放,再旋转,最后平移。
为什么是这样的顺序?因为如果最先进行平移,那么平移的成分也会被缩放;如果最后进行旋转,则物体会绕原点进行旋转而不是自身的中心。这里的证明很简单,读者可以尝试自证。
1. 复合平移变换
2. 复合缩放变换
3. 复合旋转变换
旋转变换的复合与平移和缩放有所不同。我们都知道矩阵的乘法不满足交换律,所以假设我们给定了各个轴的旋转角度zxy,即先绕z轴旋转,再绕x轴旋转,再绕y轴旋转。
关于旋转顺序,读者可以打开自己的游戏引擎调整物体的
Transform.Rotation属性自行验证。或许会有读者发现,如果直接在场景视图中使用旋转工具来对物体的各个轴旋转,那么Transform.Rotation三个维度的值并不是随着我们对旋转工具三个维度的值的调整而一致变化的,这是因为旋转工具是根据物体的模型空间坐标轴进行旋转的,而Transform.Rotation表示的是在世界空间下的旋转值,这些空间我们会在下面介绍。
坐标空间
在详细学习了常用仿射变换的性质之后,我们就可以介绍一下渲染管线中是如何使用这些变换的了。在渲染管线一文中我们曾介绍过顶点着色器阶段最基本的功能就是把模型的顶点坐标从模型空间转换到其次裁剪坐标空间中,但是并没有详细介绍这些空间的含义以及具体的变换方式。接下来我们由此引入渲染管线中的坐标空间的概念,说明为什么要使用不同的坐标空间,不同坐标空间的性质,以及渲染管线具体是如何将模型顶点在各个坐标空间之间进行转换并最终渲染到屏幕空间上的。
本文提到的模型空间,世界空间和裁剪空间均为左手坐标系,观察空间为右手坐标系。
坐标空间转换
首先先明确一个基础概念,坐标空间的转换到底如何用矩阵变换表示?
我们知道如果要定义一个坐标空间,必须定义一个原点位置和三个基向量作为坐标轴,而这些数值一定是相对于另一个坐标空间的,也就是说坐标空间之间会形成一个层次结构:每个坐标空间都是另一个坐标空间的子空间,每个坐标空间都有一个父空间。对坐标空间的变换实际上就是定义空间的父子结构,并对两个空间的点和矢量进行变换。
接下来我们进行推导:假设我们已知子坐标空间
其中
上述推导结果告诉我们:子空间基向量在父空间下的矢量表示组成的矩阵即为子空间到父空间的变换矩阵。当然,为了可以表示平移变换还需要将得到的矩阵扩展到齐次坐标空间中。
注意到
整个推导过程涉及的都是基础的矢量和矩阵运算,理解这个推导过程可以帮助读者更深刻的认识矩阵变换和齐次坐标的意义。
当仅变换方向矢量,如光照方向,法线方向时,不需要考虑平移变换的成分,这也是为什么我们常常会看到在shader中仅截取该变换矩阵的前三行三列来变换方向矢量。
接下来我们按照执行流程依次介绍渲染管线中不同的坐标空间。
1. 模型空间(model space)
每个模型都有自己独立的坐标空间,当他移动或旋转的时候,模型空间也会跟着它移动和旋转。当我们讨论模型空间的坐标时,通常指的是相对于模型自身的“自然方向”,比如“前”,“后”,“左”,“右”等等。模型空间的原点是由美术人员在建模软件中确定好的。
在顶点着色器的输入数据中,我们能访问到的顶点属性中的顶点位置矢量就是在模型空间下的顶点坐标。因此我们不需要进行任何变换,模型空间是我们的起点。
2. 世界空间(world space)
世界空间是我们所关心的最外层的坐标空间,换句话说,它通常作为渲染管线中各种坐标空间转换的父空间,这也意味着我们可以用世界空间坐标来描述”绝对位置“。在游戏引擎中,当我们调节一个没有父节点的物体的 Transform 时,其中的 Position 属性就是相对于世界空间的位置坐标。
顶点着色器中顶点变换的第一步就是将顶点坐标从模型空间转换到世界空间中,这个变换通常叫做模型变换(model transform)。模型变换是由模型在世界空间中的存在方式(位置,旋转,缩放,游戏引擎中物体的Transform组件通常会包含这一信息)定义的,通过这些数据我们就能构造出模型变换的变换矩阵,具体如何将平移,旋转和缩放构造为矩阵在前文已有详细介绍,此处不再赘述。
3. 观察空间(view space)
观察空间是指以“摄像机”为原点的坐标空间。注意,在渲染管线中摄像机并不是客观存在的物体,而是为了方便实现一些逻辑功能而引入的概念。观察空间可以视为摄像机的模型空间。
将矢量从世界空间转换到观察空间的变换叫做观察变换(view transform)。为了构造观察变换矩阵,我们需要首先需要定义摄像机在世界空间的位置信息和旋转信息。然后将对应的平移矩阵和旋转矩阵取逆后用先平移后旋转的方式复合,即可得到观察变换矩阵。
因为摄像机是通过先旋转后平移的方式从自身的模型空间变换到世界空间的,这一步本质上是对摄像机的模型变换矩阵取逆。
4. 裁剪空间(clip space)
裁剪的定义详见这篇文章。
在介绍裁剪空间之前,我们需要先了解视锥体的概念。
视锥体定义了空间中的一块裁剪区域,它决定了摄像机可以看到的范围。在视锥体的六个平面中,近裁剪平面和远裁剪平面决定了摄像机可以看到的深度范围。
视锥体分为两种类型,分别对应了两种投影方式,一种是正交投影(orthographic projection),一种是透视投影(perspective projection)。下图精确的展示了视锥体的概念。
两种投影方式对应的视锥体示意图。(左为透视投影,右为正交投影)

两种投影变换的视觉效果:透视投影模拟了人眼看世界的方式,正交投影则完全保留了物体的距离和角度。(左为透视投影,右为正交投影)
虽然我们希望根据视锥体围成的区域对图元进行裁剪,但是如果直接在观察空间进行操作,那么两种投影方式对应的不同视锥体就需要不同的处理方法,而且对于透视投影的视锥体来说,想要判断一个顶点是否在其内部是比较麻烦的,所以我们自然会想用一种更加通用的方式来完成这一步工作,即通过一个矩阵将视锥体变换为一种统一的空间格式,投影矩阵和裁剪空间应运而生。
具体的来说,投影矩阵需要完成的任务是:对x,y,z分量进行缩放,并赋予w分量特殊含义,利用w分量来判端顶点是否位于视锥体内。接下来我们看一下两种投影类型使用的投影矩阵具体是什么(以OpenGL ClipSpace为例)。
4.1 透视投影
透视投影的视锥体的六个平面是由摄像机的
借由这些信息我们可以求出视锥体近裁剪平面和远裁剪平面的高度:
而近裁剪平面和远裁剪平面的宽度则由输出窗口的横纵比
根据已知的
该矩阵的推导不在本文的讨论范围内,详情可见这篇文章。
使用该矩阵对观察空间的一个顶点进行变换,结果如下:
这个变换结果是我们关心的,这个投影矩阵首先对观察空间坐标的x,y分量进行了缩放(y与x之比等于屏幕宽度和高度之比),z分量进行缩放和平移,其次将观察空间坐标的w分量设置为了原来z分量取反的结果。现在我们就可以按如下不等式来判断任意一个裁剪空间的顶点
下图描述了观察空间中四个关键位置矢量经过透视投影矩阵变换后的坐标值。读者可以直观感受到变换的效果。
4.2 正交投影
正交投影的视锥体是一个长方体,它的六个平面是由一个长方体高度的一半(
借由这些信息我们可以求出视锥体近裁剪平面和远裁剪平面的高度:
而近裁剪平面和远裁剪平面的宽度则由输出窗口的横纵比
根据已知的
该矩阵的推导不在本文的讨论范围内,详情可见这篇文章。
使用该矩阵对观察空间的一个顶点进行变换,结果如下:
注意到和透视投影不同的是,正交投影矩阵对顶点进行变换后的w分量仍然为1,这样的选择的原因我们会在屏幕空间的部分进行介绍。
下图描述了观察空间中四个关键位置矢量经过正交投影矩阵变换后的坐标值。读者可以直观感受到变换的效果。
5. 屏幕空间
屏幕空间的坐标直接代表了真正的像素位置,是渲染管线几何处理阶段最后的坐标空间。屏幕空间是一个二维空间,因此从裁剪空间到屏幕空间是一个降维的变换,这个过程主要分为两个步骤:
- 执行齐次除法(homogeneous division),用裁剪空间中的齐次坐标的x,y,z分量除以w分量得到归一化设备坐标(Normalized Device Coordinates)。即将顶点从裁剪空间转换到了NDC空间。
- 执行屏幕映射,根据NDC坐标的x,y分量来映射输出窗口的对应像素坐标。具体来说是将
中的坐标映射到 的区域中,本质上是一个缩放的过程。
下面是齐次除法的效果示意图:
透视投影

正交投影

整个转换过程(齐次除法和屏幕映射)可以用下面的公式来总结:
Unity中,
ComputeScreenPos的计算方式为:
虽然并不是直接获得像素坐标,但是该结果的xy分量在片段着色器中进行透视除法后可以直接对屏幕空间纹理进行采样,该结果被称为ScreenUV。
从裁剪空间到屏幕空间的转换是上述所有坐标空间转换中唯一一个不需要开发者编程实现的转换过程,在顶点着色器中,开发者仅需要将顶点从模型空间转换到裁剪空间即可。
深度值
在上述工作之外,若开启了深度写入,渲染管线还会将顶点NDC坐标的z分量按照一定方式写入到深度缓冲区中,由于NDC中z分量的范围在
而深度与视角空间下的线性深度值(即视角空间下对应片段的z坐标)的关系如下:
由透视投影矩阵易知:
所以:
法线变换
前文已经讲过如何使用变换矩阵来变换一个顶点或者方向矢量,但法线是需要我们特殊处理的一种方向矢量。
我们都知道模型的顶点会携带很多顶点属性信息,比如顶点位置,顶点法线,顶点切线等。当我们变换一个模型的顶点时,不仅需要变换它的顶点位置,还需要变换它的顶点法线。但当模型变换包含非统一缩放时,如果使用和变换顶点位置同样的变换矩阵来变换法线,可能无法保证法线的垂直性。我们通过一个简单的例子来解释其中的原因:
考虑一个二维的等边直角三角形模型,我们假设其模型空间原点为直角顶点,x轴和y轴如图所示,那么其法线表示应为
![[../../../images/posts/Tech/ComputerGraphics/00000040.png]]
虽然不能统一用相同的矩阵来变换法线,但是我们可以通过数学推导来得出法线变换矩阵:
假设顶点
由于我们需要满足条件
切线是由顶点插值计算得到的,所以切线可以直接应用顶点的变换矩阵得到正确的变换结果。
FAQ TODO:
- 矩阵与向量的乘法的含义?
一般视不同的应用场景,有两种理解方式:- 将矩阵视为由行向量组成,则矩阵与向量的乘法表示向量与矩阵行向量点乘得到的数组。
- 将矩阵视为由列向量组成,则矩阵与向量的乘法表示以向量为坐标系数的矩阵列向量线性组合。
- 正交矩阵的性质如何推导?
正交矩阵由一组标准正交基组成,这意味着这组正交基中的元素与自己的点积一定为1,而与其他元素的点积一定为0,从而推导出正交矩阵的逆矩阵为自身的转置。
TODO: 写一下公式。 - 什么时候使用行矩阵?什么时候使用列矩阵?
TODO
- Title: 图形学—线性代数基础
- Author: Archer阿澈
- Created at : 2023-09-30 16:52:34
- Updated at : 2023-09-30 16:52:34
- Link: https://www.archer-du.top/2023/09/30/Tech/ComputerGraphics/Linear_Algebra/
- License: This work is licensed under CC BY-NC-SA 4.0.



