这篇笔记主要提及一些在渲染、CG、Shader 中可能会出现的数学概念。这篇笔记并不会囊括各种基础的数学概念,也几乎不会有公式,更多的是涉及概念的一些理解以及在渲染中可能会有的应用。
线性代数
向量
向量的基本概念不予赘述。以下几个概念会比较常用:
点积:因为点积的计算是两者的模相乘再乘夹角的余弦,所以如果两个向量都是单位向量,点积的结果就会是 cosθ。因此,在讨论单位向量时,我们可以将点积视为夹角的代称。
叉积:叉积的重要性质是结果与相乘的双方都垂直,右手坐标系中四指从向量 a 卷至向量 b,那么大拇指的方向就是向量 a × b 的方向。叉积可以用来找法线,也可以用来判断三角面朝向。
向量与点在形式上都是数对。以三维向量/点为例,如果单纯用数学表达法,无法判断(1,2,3)指的是一个点还是一个向量。之后我们会用齐次坐标解决这个问题。
矩阵
同样,概念上不做赘述。对于矩阵乘法的计算方式也不予赘述。以下几个概念会比较常用:
逆矩阵:通过行列式判断一个矩阵是否可逆。只要 detM ≠ 0,那么矩阵 M 就可逆。在 Shader 中我们有充足的第三方库来计算矩阵的行列式,因此这里不再赘述行列式的计算。逆矩阵用于将变换矩阵进行一次反向变换。
计算顺序:对于一个变换 CBAv,我们是对向量 v 先做 A 变换,再做 B 变换, 再做 C 变换。此处,v 必须是一个列向量。注意,大部分情况下复合变换是不满足交换律的,也就是说上面的顺序不能调整。
绝大多数情况下,我们先缩放,再旋转,最后平移。
齐次坐标
齐次坐标(homogeneous coordinate)是指在三维坐标之后加一个数值 w 以便进行计算,将仿射变换变成线性变换。齐次坐标的引入是为了解决类似于平移这样的非线性变换。
线性变换:满足这两个条件的变换 f 被称为线性变换: - f(x) + f(y) = f(x+y) - kf(x) = f(kx)
仿射变换:满足 f(x) = Ax + b 的变换都可以被叫做仿射变换。
注意:对于点,多出来的分量 w = 1;对于矢量,w = 0。如此一来,对一个点做平移变换,结果还是点;对一个矢量做平移变换,结果也还是矢量,并且实际上不会有效果。所以点和矢量在齐次坐标空间下,是有形式上的区别的。
坐标空间变换
在 Shader 中,也许坐标空间的变换是最重要的了。假设要在坐标空间 A 与 B 间相互转换。
如果要把点 x_A 从 A 坐标系转化到 B 坐标系,我们可以使用转化矩阵 MATRIX_A2B,即
x_B = MATRIX_A2B * x_A
那如果是把点 y_B 从 B 坐标系转化到 A 坐标系,我们就应该使用转化矩阵 MATRIX_B2A,即
y_A = MATRIX_B2A * y_B
这个一定要绕明白。转化矩阵是变换,肯定要放在向量的左侧进行左乘。转化矩阵是从原坐标系转换到目标坐标系的转化矩阵。所以我们就会有上面的结果。
通常来说,我们在 Shader 中遇到的各类坐标系的转化矩阵已经有内置。我们无非就是要熟知在写 Shader 时可能会遇到多少种不同的坐标系。
顶点变换过程
在 Unity 以及其它的各种计算机图形渲染中,一个模型上的顶点最终肯定需要通过多次变换才能到达屏幕上方,在一个像素中被渲染出来。
在这个过程中,它最初处于模型的模型空间上,接下来会被变换到世界空间上,然后会被变换到观测空间上,接着是在裁剪空间,最终被变换到屏幕空间上。
模型空间
模型空间(Model Space)也叫本地空间/局部空间(Local Space)。美术在建模的时候,会将某一个点作为原点,并且设定好 x,y,z 轴的正方向。我们在 Unity 中点击一个物体,直接跳出来的这三个轴和轴心基本也表示了模型空间的三轴正方向和原点。这个物体上的每一个点针对这个原点和三轴都会有自己的坐标。
世界空间及模型至世界变换
通常来说,世界空间就是 Unity 中 “最外层” 的空间。如果一个 GameObject 没有任何的 Parent Object,那我们在它的 Transform 的 Position 就是它在世界空间的坐标。
模型至世界变换 / 模型变换
模型变换(Model Transform, M)指的是将顶点从模型空间转换到世界空间的变换。在 Unity 中,UnityShaderVariables.cginc 文件中自带的一个定义过的变量 _Object2World 左乘模型空间坐标即可将坐标从模型空间转换到世界空间上。我们可以简称这个矩阵为 M 矩阵。
观测空间及世界至观测变换
观测空间(View Space)也叫摄像机空间。以摄像机为原点,+x 轴为摄像机的右方,+y 轴为摄像机的上方, +z 轴指向摄像机的后方建立的坐标系。换言之,摄像机的正前方指向观测空间的 -z 方向,这说明观测空间使用的旋向与之前两个空间不同。前二者为左手坐标系,而观测空间为右手坐标系。
世界至观测变换 / 观察变换
观察变换(View Transform, V)指的是将顶点从世界空间转换到观测空间的变换。在 Unity 中,UnityShaderVariables.cginc 文件中自带的一个定义过的变量 UNITY_MATRIX_V 左乘世界空间坐标即可将坐标从世界空间转换到观测空间上。我们可以简称这个矩阵为 V 矩阵。
裁剪空间及世界至观测变换
裁剪空间(Clip Space, P),也叫齐次裁剪空间。裁剪空间的作用是将不在视锥体的图元剔除,将与视锥体边界相交的图元进行裁剪。
视锥体(View Frustum)指的就是 Unity 中相机的这个可视范围,由 6 个平面围成,离相机最近的平面叫 Near Clip 平面,最远的则叫 Far Clip 平面。
Unity 中有两种渲染模式,在 Orthographic (正交投影)模式下,远近平面大小一致,而在 Perspective (透视投影)模式下, 远平面比近平面要大。
刚才提到,对于一个顶点来说,齐次坐标的 w 分量为 1 。在 Unity 中,你可以通过 UNITY_MATRIX_P 左乘观测空间坐标,就可以将顶点从观测空间转换到裁剪空间。正交投影与透视投影对应的投影矩阵并不同,具体的推导在这里不予赘述,需要知道它们的区别在于:
- 正交投影下,w 分量会被投影矩阵转化为 -z,对 xyz 进行不同程度的缩放。
- 透视投影下,w 分量依然是 1 。
透视矩阵会修改空间旋向性,从右手坐标系转到左手坐标系,所以离摄像机越远,z 越大。
透视矩阵与相机的远近平面位置、相机的纵横比(Aspect Ratio)以及张开角度(Field of View)都有关。
裁剪空间至屏幕空间
最后一步,就是要将顶点转换到 2D 的屏幕空间上。这个步骤分两步:
第一步 齐次除法
将 x,y,z 变换为 x/w, y/w, z/w。这一步得到的坐标也叫做归一化设备坐标( NDC 坐标)。视锥体在这个变换下会变成一个立方体。
第二步 屏幕映射
在 Unity 中,经过第一步之后,当前 x, y 的坐标都已经落在 [-1,1] 之间了。我们只需要把这个 (x,y) 映射到一个 x ∈ [0, width], y ∈ [0, height] 的屏幕空间即可。
此时,z 分量可能会用于深度缓存。
Comentários