学习Shader所需的数学基础
笛卡尔坐标系
二维笛卡尔坐标系
一个二维的笛卡尔坐标系包含了两个部分的信息:
- 一个特殊的位置,即原点,它是整个坐标系的中心
- 两条过原点的互相垂直的矢量,即 x x x轴和 y y y轴。这些坐标轴也被称为是该坐标系的基矢量
坐标系中的 x x x轴和 y y y轴不一定是水平和垂直方向的
有了这个坐标系就可以精确的定位一个点的位置
三维笛卡尔坐标系
三维笛卡尔坐标系相较于二维来说要复杂许多,但这并不意味着很难学会他,对人类来说,生活的世界就是三维的,因此对于理解更低维度的空间(一维和二维)是比较容易地。而对于同等维度的一些概念;理解起来难度就大一些;对于更高维度的空间(如四维空间),理解难度就更大了
在三维笛卡尔坐标系中需要定义3个坐标轴和一个原点,如图
这3个坐标轴也被称为是该坐标系的基矢量,通常情况下,这3个坐标轴之间是相互垂直的,且长度为1,这样的基矢量被称为标准正交基,但这并不是必须的。例如,在一些坐标系中坐标轴之间互相垂直但长度不为1,这样的基矢量被称为正交基。如非特殊说明,默认情况下使用的坐标轴指的都是标准正交基
正交可以理解成相互垂直的意思
和二维笛卡尔坐标系类似,三位笛卡尔坐标系中的坐标轴方向也不是固定的,即不一定是像上图中那样的指向。但这种不同导致了两种不同种类的坐标系:左手坐标系和右手坐标系
左手坐标系和右手坐标系
为什么在三维笛卡尔坐标系中要区分左手坐标系和右手坐标系,而二维中就没有这些事情?这是因为,在二维笛卡尔坐标系中, x x x轴和 y y y轴的只想虽然可能不同,但总可以通过一些旋转操作来使它们的坐标轴指向相同
对于三维笛卡尔坐标系,靠旋转并不能使两个不同朝向的坐标系重合。也就是说,三维笛卡尔坐标系并不都是等价的。因此,就出现了两种不同的三维坐标系:左手坐标系和右手坐标系。如果两个坐标系具有相同的旋向性,那么就可以通过旋转的方法来让它们的坐标系重合。但是,如果他们具有不同的旋向性,那么就无法达到重合的目的
为什么叫左手坐标系和右手坐标系?这是因为,可以利用双手来判断一个坐标轴的旋向性,举起左手,用食指和大拇指摆出一个”L“的收拾,并且让食指指向上,大拇指指向右。现在伸出中指,不出意外的话它应该指向前方,这就是左手坐标系;同样的,举起右手,食指指向上,中指指向前方,拇指指向左侧,这就是右手坐标系,如图
除了坐标系朝向不同之外,左手坐标系和右手坐标系对于正向旋转的定义也不同,即在出高中物理中学到的左手法则和右手法则。
左右手坐标系是可以进行相互转换的。最简单的方法就是把其中一个轴反转,并保持其他两个轴不变
对于开发者来说,使用左手坐标系还是右手坐标系都是可以的,它们之间并没有优劣之分。无论使用哪种坐标系,绝大多数情况下并不会影响底层的数学运算,而只是在映射到视觉上时会有差别。这是因为一个点或者旋转在空间内来说时绝对的
Unity使用的坐标系
Unity使用的时左手坐标系,这可以从Scene试图的坐标轴显示可以看出来,如图
这意味着,在模型空间中,一个物体的右侧、上侧和前侧分别对应了 x x x轴, y y y轴, z z z轴的正方向
但对于观察空间来说,Unity使用的是右手坐标系。观察空间通俗来讲就是以摄像机为原点的坐标系。在这个坐标系中,摄像机的前向时 z z z轴的负方向,这与在模型空间和世界空间中的定义相反。也就是说, z z z轴坐标的减少意味着场景深度的增加,如图
在Unity中,观察空间使用的是右手坐标系,摄像机的前向是 z z z轴的负方向, z z z轴越小,物体的深度越大,离摄像机越远
点和矢量
点是 n n n维空间中的一个位置,他没有大小、宽度这类概念。在笛卡尔坐标系中,可以使用2个或3个实数来表示一个点的坐标
矢量的定义则复杂一些。矢量存在的意义更多是为了和标量区分开来。通常来讲,矢量是指 n n n维空间中一种包含了模和方向的有向线段
具体来讲
- 矢量的模指的是这个矢量的长度。一个矢量的长度可以是任意的非负数
- 矢量的方向则描述了这个矢量在空间中的指向。
矢量的表示方法和点类似。可以使用 v = ( x , y ) v = (x,y) v=(x,y)来表示二维矢量,用 v = ( x , y , z ) v=(x,y,z) v=(x,y,z)来表示三维矢量,用 v = ( x , y , z , w ) v=(x,y,z,w) v=(x,y,z,w)来表示四维矢量
一个适量通常由一个箭头表示,有时会讲到一个矢量的头和尾。矢量的头指的是它的箭头所在的端点处,而尾指的是另一个端点处,如图
通常,矢量被用于表示相对于某个点的偏移,也就是说它是一个相对量。只要矢量的模和方向保持不变,无论在哪里都是一个矢量
点和适量的区别
上节提到矢量通常用于描述偏移量,因此,它们可以用于描述相对位置,即相对于另一个点的位置,此时矢量的尾是一个位置,那么矢量的头就可以表示另一个位置了。而一个点可以用于指定空间中的一个位置(即相对于原点的位置)。如果把矢量的尾固定在坐标系原点,那么这个矢量的表示就和点的表示重合了,如图
矢量运算
1. 矢量和标量乘法/除法
虽然不能把矢量和标量进行相加/相减的运算,但可以对他们进行乘法运算,结果会得到一个不同长度且可能方向相反的新的矢量
公式非常简单,只需要把矢量的每个分量和标量相乘即可:
k v = ( k v x , k v y , k v z ) kv = (kv_x,kv_y,kv_z) kv=(kvx,kvy,kvz)
类似的,一个适量也可以被一个非零的标量除。这等同于和这个标量的倒数相乘:
v k = ( x , y , z ) k = 1 k ( x , y , z ) = ( x k , y k , z k ) , k ≠ 0 \frac{\mathbf{v}}{k} = \frac{(x,y,z)}{k} = \frac{1}{k}(x,y,z) = \left(\frac{x}{k},\frac{y}{k},\frac{z}{k}\right),k \neq 0 kv=k(x,y,z)=k1(x,y,z)=(kx,ky,kz),k=0
注意,对于惩罚来说,矢量和标量的位置可以互换,但对于出发,只能是矢量被标量除,而不能被矢量除,这是没有意义的
从几何意义上看,把一个 v \mathbf{v} v和一个标量 k k k相乘,意味着对矢量 v \mathbf{v} v进行一个大小为 ∣ k ∣ |k| ∣k∣的缩放,如下图例
2.矢量的加法和减法
可以对两个矢量进行相加或相减,其结果是一个相同维度的新矢量
只需要把两个矢量的对应分量进行相加或相减即可。公式如下
a + b = ( a x + b x , a y + b y , a z + b z ) a − b = ( a x − b x , a y − b y , a z − b z ) \begin{aligned}\mathbf{a} + \mathbf{b} = (a_x+b_x,a_y+b_y,a_z+b_z)\\ \mathbf{a} - \mathbf{b} = (a_x-b_x,a_y-b_y,a_z-b_z)\end{aligned} a+b=(ax+bx,ay+by,az+bz)a−b=(ax−bx,ay−by,az−bz)
需要注意的是,一个矢量不可以和一个标量相加或相减,或者是和不同维度的矢量进行运算
从几何意义上来看,对于减法,可以把矢量 a \mathbf{a} a的头连接到矢量 b \mathbf{b} b的尾,然后画一条从 a \mathbf{a} a的尾到 b \mathbf{b} b的头的矢量,来得到 a \mathbf{a} a和 b \mathbf{b} b相加后的矢量。也就是说,如果从一个起点开始进行了一个位置偏移 a \mathbf{a} a,然后又进行了一个位置偏移 b \mathbf{b} b,那么就等同于进行了一个 a + b \mathbf{a+b} a+b的位置偏移。这被称为矢量加法的三角形定则。矢量的减法是类似的,如图
需要时刻谨记,在图形学中矢量通常用于描述位置偏移(简称位移)。因此,可以利用适量的加法和减法来计算一点相对于另一点的位移
假设,空间内有两点 a a a和 b b b,如果想要计算点 b b b相对于点 a a a的位移,就可以通过把 b b b和 a a a相减得到,如图
3.矢量的模
矢量的模是一个标量,可以理解为是矢量在空间中的长度。它的表示符号通常是在矢量两旁分别加上一条垂直线(有的文献中会使用两条垂直线)。三维矢量的模的计算公式如下
∣ v ∣ = v x 2 + v y 2 + v z 2 |\mathbf{v}| = \sqrt{v_x^2+v_y^2+v_z^2} ∣v∣=vx2+vy2+vz2
其他维度的矢量的模计算类似,都是对每个分量的平方相加后再开根号得到
可以从几何意义来理解上述公式。对于二维矢量来说,可以对任意矢量构建一个三角形,如图
由图可以看出,对于二维矢量,其实就是使用了勾股定理,矢量的两个分量的绝对值对应了三角形两个直角边的长度,而斜边的长度,而斜边的长度就是矢量的模
4.单位矢量
单位矢量指的是那些模为 1 的矢量。单位矢量也被称为归一化的矢量。对任何给定的非零矢量,把它转换成单位矢量的过程就被称为归一化
给定任意非零矢量 v \mathbf{v} v,可以计算和 v \mathbf{v} v方向相同的单位矢量。为了对矢量进行归一化,可以用矢量初一该矢量的模来得到。公式如下:
v ^ = v ∣ v ∣ , v 是任意非零矢量 \mathbf{\hat v} = \mathbf{\frac{v}{|v|}},\mathbf{v}是任意非零矢量 v^=∣v∣v,v是任意非零矢量
零矢量(即矢量的每个分量值都为0)是不可以被归一化的。这是因为做除法运算时分母不为0.
从几何意义上看,对二维空间来说,可以画一个单位圆,那么单位矢量就可以是从圆心出发、到圆边界的矢量,在三维空间中,单位矢量就是从一个单位球的求新出发、到达球面的矢量。如下图给出了二维空间内的一些单位矢量。
5.矢量的点积
适量之间也可以进行乘法,但是和标量之间的乘法有很大不同。矢量的乘法有两种最常用的种类:点积(也被称为内积)和叉积(也被称为外积)
点积的名称来源于这个运算的符号: a ⋅ b \mathbf{a} \cdot \mathbf{b} a⋅b。中间的圆点符号是不可以省略的。点积的公式有两种形式,先看第一种。两个三维矢量的点积是把两个矢量对应分量相乘后再取和,最后的结果是一个标量
公式一:
a ⋅ b = ( a x , a y , a z ) ⋅ ( b x , b y , b z ) = a x b x + a y b y + a z b z \mathbf{a} \cdot \mathbf{b} = (a_x,a_y,a_z) \cdot (b_x,b_y,b_z) = a_xb_x+a_yb_y+a_zb_z a⋅b=(ax,ay,az)⋅(bx,by,bz)=axbx+ayby+azbz
矢量的点积满足交换律,即 a ⋅ b = b ⋅ a \mathbf{a \cdot b = b \cdot a} a⋅b=b⋅a
点积的几何意义很重要,因为点积几乎应用到了图形学的各个方面。其中一个几何意义就是投影
假设,有一个单位矢量 a ^ \mathbf{\hat {a}} a^和另一个长度不限的矢量 b \mathbf{b} b。现在,希望得到 b \mathbf{b} b在平行于 a ^ \mathbf{\hat{a}} a^的一条直线上的投影。那么,就可以使用点积 a ^ ⋅ b \mathbf{\hat{a} \cdot b} a^⋅b来得到 b \mathbf{b} b在 a ^ \mathbf{\hat{a}} a^方向上的有符号的阴影
投影是什么意思?一个通俗的解释为:现在有一个光源,它发出的光线是垂直于 a ^ \mathbf{\hat{a}} a^方向的,那么 b \mathbf{b} b在 a ^ \mathbf{\hat{a}} a^方向上的投影就是 b \mathbf{b} b在 a ^ \mathbf{\hat{a}} a^方向上的影子,如图所示
需要注意的是,投影的值可能是附属。投影结果的正负号与 a ^ \mathbf{\hat{a}} a^和 b \mathbf{b} b方向有关:当它们的方向相反(夹角大于 9 0 ∘ 90^\circ 90∘)时,结果小于0;当它们的方向相互垂直(夹角为 9 0 ∘ 90^\circ 90∘)时,结果等于0;当它们的方向相同(夹角小于 9 0 ∘ 90^\circ 90∘)时,结果大于0,如图所示
也就是说,点积的符号可以让我们知道两个适量的方向关系
那么,如果 a ^ \mathbf{\hat{a}} a^不是一个单位矢量会如何?任何两个矢量的点积 a ⋅ b \mathbf{a \cdot b} a⋅b等同于 b \mathbf{b} b在 a \mathbf{a} a方向上的投影值,再乘以 a \mathbf{a} a的长度
点积有一些很重要的性质:
性质一:点积可结合标量乘法
上面的“结合”是说,点积的操作数之一可以是另一个运算的结果,即矢量和标量相乘的结果。公式如下:
( k a ) ⋅ b = a ⋅ ( k b ) = k ( a ⋅ b ) (k\mathbf{a})\cdot \mathbf{b} = \mathbf{a}\cdot(k\mathbf{b}) = k(\mathbf{a \cdot b}) (ka)⋅b=a⋅(kb)=k(a⋅b)
也就是说,对点积中其中一个矢量进行缩放的结果,相当于对最后的点积结果进行缩放
性质二:点积可结合矢量加法和减法,和性质一类似
这里的“结合”指的是,点积的操作数可以是矢量相加或相减后的结果。用公式表达如下:
a ⋅ ( b + c ) = a ⋅ b + a ⋅ c \mathbf{a\cdot(b+c)}=\mathbf{a\cdot b + a\cdot c} a⋅(b+c)=a⋅b+a⋅c
把上面的 c \mathbf{c} c换成 − c \mathbf{-c} −c就可以得到减法的版本
性质三:一个适量和本身进行点积的结果,是该矢量的模的平方
这点可以很容易从公式验证中得到:
v ⋅ v = v x v x + v y v y + v z v z = ∣ v ∣ 2 \mathbf{v\cdot v} = v_xv_x + v_yv_y + v_zv_z = |\mathbf{v}|^2 v⋅v=vxvx+vyvy+vzvz=∣v∣2
这意味着,可以直接利用点击来求矢量的模,而不需要使用模的计算公式。淡然,需要对点积结果进行开平方的操作来得到真正的模。但很多情况下,只是想要比较两个向量的长度大小,因此可以直接使用点积的结果。毕竟,开平方的运算需要消耗一定的性能
点积的另一种表示方法。这种方法时从三角代数的角度出发的,这种表示方法更加具有几何意义,因此它可以明确的强调出两个矢量之间的角度
公式二:
a ⋅ b = ∣ a ∣ ∣ b ∣ cos θ \mathbf{a \cdot b}=|\mathbf{a}||\mathbf{b}|\cos \theta a⋅b=∣a∣∣b∣cosθ
两个矢量的点积可以表示为两个矢量的模相乘,再乘以他们之间夹角的余弦值,从这个公式也可以看出,为什么计算投影时两个矢量的方向不同会得到不同符号的投影值:当夹角小于 9 0 ∘ 90^\circ 90∘时, cos θ > 0 \cos\theta>0 cosθ>0;当夹角等于 9 0 ∘ 90^\circ 90∘时, cos θ = 0 \cos\theta=0 cosθ=0;当夹角大于 9 0 ∘ 90^\circ 90∘时, cos θ < 0 \cos\theta<0 cosθ<0
利用这个公式还可以求得两个向量之间的夹角(在 0 ∘ ∼ 18 0 ∘ 0^\circ \sim 180^\circ 0∘∼180∘):
θ = a r c o s ( a ^ ⋅ b ^ ) , 假设 a ^ 和 b ^ 时单位矢量 \theta=arcos(\mathbf{\hat{a}\cdot\hat{b}}),假设\mathbf{\hat{a}}和\mathbf{\hat{b}}时单位矢量 θ=arcos(a^⋅b^),假设a^和b^时单位矢量
其中,arcos是反余弦函数
6.矢量的叉积
另一个重要的矢量运算就是叉积,也被称为外积。与点积不同的是,矢量叉积的结果仍是一个矢量,而非标量
和点积类似,叉积的名称来源于它的符号: a × b \mathbf{a \times b} a×b。同样,这个叉号也是不可省略的。两个矢量的叉积可以用如下公式计算:
a × b = ( a x , a y , a z ) × ( b x , b y , b z ) = ( a y b z − a z b y , a z b x − a x b z , a x b y − a y b x ) \mathbf{a \times b} = (a_x,a_y,a_z)\times(b_x,b_y,b_z)=(a_yb_z-a_zb_y,a_zb_x-a_xb_z,a_xb_y-a_yb_x) a×b=(ax,ay,az)×(bx,by,bz)=(aybz−azby,azbx−axbz,axby−aybx)
上面的公式看起来很复杂,但其实是有一定规律的,如图所示:
需要注意的是,叉积不满足交换律,即 a × b ≠ b × a \mathbf{a \times b \neq b \times a} a×b=b×a。实际上,叉积是满足反交换律的,即 a × b = − ( b × a ) \mathbf{a\times b = -(b \times a)} a×b=−(b×a)。而且叉积也不满足结合律,即 ( a × b ) × c ≠ a × ( b × c ) (\mathbf{a \times b})\times c \neq \mathbf{a\times(b\times c)} (a×b)×c=a×(b×c)
从叉积的几何意义出发,可以更加深入的了解它的用处。对两个矢量进行叉积的结果会得到一个同时垂直于这两个矢量的新矢量
叉积有什么作用?最常见的一个应用就是计算垂直于一个平面、三角形的矢量。另外,还可以用于判断三角面片的朝向
矩阵
矩阵的定义
矩阵是由 m × n m \times n m×n个标量组成的长方形数组。既然是网状结构,就意味着矩阵由行列之分,据此,可以给出矩阵的一般表达式,如下:
[ m 11 m 12 m 13 m 21 m 22 m 23 m 31 m 32 m 33 ] \begin{bmatrix} m_{11}&m_{12}&m_{13}\\ m_{21}&m_{22}&m_{23}\\ m_{31}&m_{32}&m_{33} \end{bmatrix} m11m21m31m12m22m32m13m23m33
m i j \mathbf{m_{ij}} mij表明了这个元素在矩阵 M \mathbf{M} M的第 i i i行、第 j j j列
和矢量联系起来
矢量可以看成是 n × 1 n\times1 n×1的列矩阵或 1 × n 1\times n 1×n的行矩阵,其中 n n n对应了矢量的维度
为什么要把矢量和矩阵联系在一起?这是为了可以让矢量像一个矩阵一样一起参与矩阵运算。这在空间变换中非常有用
矩阵运算
1.矩阵和标量的乘法
和矢量类似,矩阵也可以和标量相乘,它的结果仍然是一个相同维度的矩阵。它们之间的乘法非常简单,就是矩阵的每个元素和该标量相乘。以 3 × 3 3\times 3 3×3的矩阵为例,其公式如下:
k M = M k = k [ m 11 m 12 m 13 m 21 m 22 m 23 m 31 m 32 m 33 ] = [ k m 11 k m 12 k m 13 k m 21 k m 22 k m 23 k m 31 k m 32 k m 33 ] k\mathbf{M} = \mathbf{Mk} = k\begin{bmatrix} m_{11}&m_{12}&m_{13}\\ m_{21}&m_{22}&m_{23}\\ m_{31}&m_{32}&m_{33} \end{bmatrix} = \begin{bmatrix} km_{11}&km_{12}&km_{13}\\ km_{21}&km_{22}&km_{23}\\ km_{31}&km_{32}&km_{33} \end{bmatrix} kM=Mk=k m11