Unity Shader读书笔记----第四章<数学基础>

# Shader数学基础

先发个个人感想

Typora设置相对路径配上OneDrive之后就实现了其他Markdown编辑器的付费同步效果。主题也可以自己下载CSS或者自己配置，真是物超所值(然而为了发博客，还是下了个iPic来上传图片)

另外这一章基本上都是高中数学知识，简单做一些笔记，顺便复习一下LaTex :smile_cat:

## 笛卡尔坐标系

![image-20190726111913727](http://ww3.sinaimg.cn/large/006tNc79gy1g5d0y2hv3uj30g40f60ut.jpg)

x,y,z轴也称为该坐标系的基矢量（basic vector). 因为其长度为1且互相垂直，这样的基矢量称为标准正交基

Unity使用的是左手坐标系，对于观察空间，Unity使用的是右手坐标系（毕竟对摄像机来说是镜像）

### 练习题解答

1. ~~左手坐标系~~右手坐标系
2. (1,0,0) (1,0,0)
3. -10, 10

## 点和矢量

### 定义

**点**：*一个位置，没有大小，宽度等观念*

**矢量**：*包含模(magnitude)和方向(direction)*. 

矢量没有位置信息。被用于表示相对于某个点的偏移(displacement)

任何一个点都可以表示为从原点出发的矢量



### 运算

#### 1. 矢量和标量的乘除法

k$\vec{a}$ = ($kv_j, kv_y, kv_z$);

乘法，矢量和标量的位置可以互换。除法只能是矢量被标量除。
$$
k\vec{a} = (kv_j, kv_y, kv_z);\\
\frac{\vec{v}}{k} = \frac{(x,y,z)}{k} = (\frac{x}{k}, \frac{y}{k}, \frac{z}{k}), k≠0
$$

#### 2. 矢量的加减法

一个矢量不可以和一个标量相加或相减，或者是和不同维度的矢量进行运算。

#### 3. 矢量的模

$$
|\vec{v}|=\sqrt{v_x^2 + v_y^2 + v_z^2} 
$$

#### 4.单位矢量(unit vector)

很多时候我们只关心矢量的方向而不是模，这种情况下我们需要计算单位矢量。

单位矢量是模为1的矢量。也称为被归一化的矢量(normalized vector)。讲非零矢量转换成单位矢量的过程被称为归一化(normalization)。

$\hat{\mathbf{v}}$可以用来表示单位向量。 公式如下：
$$
\hat{\mathbf{v}}=\frac{\mathbf{v}}{|\mathbf{v}|}
$$
**零矢量**是不能被归一化的。

#### 5.矢量点积(dot product)

点积的结果是一个标量

几何意义：向量b在向量a方向上的投影值，再乘以向量a的长度

公式一：
$$
\vec{a}\cdot \vec{b} = (a_x,a_y,a_z) \cdot (b_x,b_y,b_z)=a_xb_x+a_yb_y+a_zb_z​
$$
公式二：
$$
\vec{a}\cdot\vec{b}=|\vec{a}||\vec{b}|cos\theta\\
\theta=arcos(\hat{\vec{a}}\cdot\hat{\vec{b}})
$$
![image-20190726171656828](http://ww4.sinaimg.cn/large/006tNc79gy1g5dba7apqqj31ee0iqdnr.jpg)

#### 6.矢量的叉积(cross product)

叉积的结果是一个矢量。这个新矢量同时垂直于这两个矢量。

新矢量的方向取决于坐标系的类型（左手右手）

公式：
$$
\vec{a} \times \vec{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)
$$
模：
$$
|\vec{a} \times \vec{b}|=|\vec{a}||\vec{b}|sin\theta
$$

#### 练习题解答

1.~~true~~, ~~false~~, false

2. sqrt(62), (12.5, 10, 25), (1.5,2),(5/13, 12/13), (1/sqrt(3),1/sqrt(3),1/sqrt(3)), (10,9),(-6,1,2)
3. sqrt(308)
4. 75,
5. 12, 20.784
6. (x-p)v 正为前面，负为后面。-2。arccos来计算角度。再计算模
7. （p1-p2)x(p2-p3) 正为顺时针，负为逆时针

## 矩阵

矩阵就是$m \times n$ 个标量组成的长方形数组。

矩阵乘法中第一个矩阵的列数必须和第二个矩阵的行数相同。结果矩阵的行数是第一个矩阵的行数，列数是第二个矩阵的列数。
$$
\begin{eqnarray}
 c_{ij}
 = a_{i1}b{1j}+a_{i2}b_{2j}+\cdots+a_{in}b_{nj}
 = \sum_{ k = 1 }^{ n } a_{ik}b_{kj}
\end{eqnarray}
$$
矩阵乘法通常情况下不满足交换律。满足结合律。

Shader中一般使用$4\times 4$的矩阵。

一些特殊矩阵：

##### 1. 方块矩阵(square matrix)

行和列数目想等的矩阵，简称方阵。

对角矩阵(diagonal matrix):
$$
\begin{bmatrix}
	3 & 0 & 0 & 0\\
	0 & -2 & 0 & 0 \\
	0 & 0 & 1 & 0 \\
	0 & 0 & 0 & 7
\end{bmatrix}
$$

2. ##### 单位矩阵(identity matrix)

一般用$\mathit{I}_n$ 来表示。任何矩阵和它相乘的结果还是原来的矩阵，
$$
\mathit{I}_3 = 
\begin{bmatrix}
	1 & 0 & 0\\
	0 & 1 & 0  \\
	0 & 0 & 1
\end{bmatrix}
$$

##### 3. 转置矩阵(transposed matrix)

转置矩阵实际上是对原矩阵的转置运算。
$$
M^{\mathrm{T}}_{ij} = M_{ji}
$$
一些性质：

1. 矩阵转置的转置等于原矩阵

2. 矩阵串接的转置等于反向串接各个矩阵的转置
   $$
   (AB)^{\mathrm{T}}=B^{\mathrm{T}}A^{\mathrm{T}}
   $$

##### 4. 逆矩阵(inverse matrix)

不是所有矩阵都有逆矩阵。判断条件为：必须是方阵。且行列式(determinant)不为0。

逆矩阵最重要的性质是矩阵与逆矩阵相乘，结果是一个单位矩阵。

几何意义：一个矩阵可以表示一个变换，而逆矩阵允许还原这个变换。
$$
M^{-1}(Mv)=(M^{-1}M)v=Iv=v
$$
一些性质：

1. 逆矩阵的逆矩阵是原矩阵

2. 单位矩阵的逆矩阵是它本身

3. 转置矩阵的逆矩阵是逆矩阵的转置
   $$
   (M^{\mathrm{T}})^{-1} = (M^{-1})^{\mathrm{T}}
   $$

4. 矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵

##### 5. 正交矩阵(orthogonal matrix)

一种特殊的方阵。正交是矩阵的一种属性。如果一个方阵$M$和它的转置矩阵的乘积是单位矩阵的话，那么这个矩阵是正交的。反过来也成立。
$$
MM^{\mathrm{T}}=M^{\mathrm{T}}M=I
$$
于是我们还能知道，如果一个矩阵是正交的，那么它的转置矩阵和逆矩阵是一样的。

有正交矩阵的条件：

每一行都是单位矢量，且每一行之间互相垂直。每一列也是如此

标准正交基是常见的正交矩阵

#### 行矩阵还是列矩阵

因为行矩阵和列矩阵影响乘法结果，所以很重要。

Unity中常规做法是把矢量放在矩阵的右侧，即把矢量转换成列矩阵来进行计算。

使用列矩阵的结果是，我们的阅读顺序是从右到左：
$$
CBAv=(C(B(Av)))
$$
等价于
$$
vA^{\mathrm{T}}B^{\mathrm{T}}C^{\mathrm{T}}=(((vA^{\mathrm{T}})B^{\mathrm{T}})C^{\mathrm{T}})
$$

#### 练习题解答

1. $$
   \begin{bmatrix}
   	-1 & 11 \\
   	-2 & 18
   \end{bmatrix}
   $$

   unable,
   $$
   \begin{bmatrix}
   	11  \\
   	11 \\
       -6
   \end{bmatrix}
   $$

2. false, true, true

3. 一样，不一样，一样

 

## 变换(Transform)

矩阵的几何意义就是变换。

### 变换的定义

把数据通过某种方式进行转换的过程。

**仿射变换(affine transform)**是合并线性变换和平移变换的类型。可以用用一个4x4的矩阵来表示。

为此，我们需要把矢量扩展到四维空间，也就是**齐次空间(homogeneous space)**

![1564235600027](http://ww4.sinaimg.cn/large/006tNc79gy1g5gpe21nq6j30of0930x8.jpg)

### 矩阵的变换

平移矩阵不是正交矩阵

缩放矩阵一般不是正交矩阵

旋转矩阵是正交矩阵

符合变换公式：
$$
P_{new}= M_{translation}M_{rotation}M_{scale}P_{old}
$$
一般来说变换的顺序是缩放，旋转，最后是平移

其中旋转的变换顺序是zxy

## 坐标空间

渲染游戏的过程可以理解成一个个顶点经过层层处理最终转化到屏幕上的过程。

### 坐标空间的转换

每个坐标空间都是另一个坐标空间的子空间，反过来，每个空间都有一个父坐标空间。对坐标空间的变换实际上就是在父空间和子空间之间对点和矢量进行变换。
$$
A_p =M_{c \rightarrow p}A_c \\
B_p = M_{p \rightarrow c}B_p
$$
p代表父空间，c代表子空间 $M_{c \rightarrow p}$表示的是从子坐标空间变换到父坐标空间的变换矩阵，而$M_{p \rightarrow c}$是其逆矩阵。

$M_{c \rightarrow p}$推导公式：

![image-20190729163745852](http://ww2.sinaimg.cn/large/006tNc79gy1g5gr0cz6l7j31dg0sy14o.jpg)

因为矢量不受平移的影响，所以对矢量进行变换的时候只需要$3 \times 3$的矩阵就即可。

### 模型空间(model space)

模型空间是和某个模型或者说是对象有关的。有时也称为**对象空间(object space)**或者**局部空间(local space)**。每个模型都有自己独立的坐标空间。

当模型移动或者旋转的时候，模型空间也会跟着它移动和旋转

### 世界空间(world space)

世界空间是我们所关心的最大的空间。被用于描述绝对位置。

Unity中世界空间的坐标轴是固定不变的。

### 观察空间(view space)

​		也称为**摄像机空间(camera space)**。观察空间可以被认为是一个特例的模型空间。

​		摄像机决定了我们渲染游戏所使用的视角。Unity中的观察空间使用的是右手坐标系。

​		观察空间和屏幕空间是不同的。观察空间到屏幕空间需要**投影(projection)**。

获得顶点在观察空间中的位置有两种操作：

1. 计算观察空间的3个坐标轴在世界空间下的表示，构建出观察空间变换到世界空间的变换矩阵，再对该矩阵求逆得到从世界空间变换到观察空间的矩阵
2. 平移观察空间，令摄像机原点位于世界坐标的远点。

摄像机的变换矩阵需要对z分量进行取反操作。

### 裁剪空间(clip space)

用于变换的矩阵叫做裁剪矩阵(clip matrix)，也被称为**投影矩阵(projection matrix)**。

裁剪空间的目标是为了方便地对渲染图元进行裁剪。这块空间是由**视锥体(view frustum)**决定的。

视锥体由六个平面包围而成，这些平面也被称为裁剪平面(clip planes)。决定了摄像机可以看到的空间。视锥体有两种类型正交投影(orthographic projection)和透视投影(perspective projection)。

![image-20190729185530023](http://ww4.sinaimg.cn/large/006tNc79gy1g5guzngpl2j31eu0pe7wh.jpg)

投影矩阵的名称虽然包含了投影，但是它并没有进行真正的投影工作，而是在为投影做准备。真正的投影发生在后面的**齐次除法(homogeneous division)**过程中。

投影可以被理解为一个空间的降维。

#### 1.透视投影

Field Of View决定视锥体垂直方向的张开角度。Near和Far决定近裁剪平面和远裁剪平面的远近

![image-20190729190927299](http://ww3.sinaimg.cn/large/006tNc79gy1g5gve6c3plj30ok0ncgth.jpg)

摄像机的横纵比由Game视图的横纵比和Viewport Rect中的W和H属性共同决定。也可以通过Script对Camera.aspect进行更改。

![image-20190729191007017](http://ww2.sinaimg.cn/large/006tNc79gy1g5gvev2ws6j30ow06qjtz.jpg)

由上述信息，可以确定透视投影的投影矩阵

![image-20190729191405337](http://ww1.sinaimg.cn/large/006tNc79gy1g5gvj01w91j30ze0dmn1c.jpg)

![image-20190729192019241](http://ww2.sinaimg.cn/large/006tNc79gy1g5gvpi23ktj31960tc7g7.jpg)

这个投影本质就是对x,y和z分量进行了不同程度的缩放（z分量还做了一个平移）

![image-20190729192857687](http://ww3.sinaimg.cn/large/006tNc79gy1g5gvygt5znj30ws07040u.jpg)

#### 2.正交投影

正交投影的视锥体是一个长方体。

![image-20190729192940098](http://ww2.sinaimg.cn/large/006tNc79gy1g5gvz71v5fj30n80jcdl5.jpg)

纵向信息：

![image-20190729193110606](http://ww1.sinaimg.cn/large/006tNc79gy1g5gw0rms68j30p404wac0.jpg)

横向信息，Aspect为摄像机的横纵比：
$$
nearClipPlaneWidth=Aspect\cdot nearClipPlaneHeight \\
farClipPlaneWidth = nearClipPlaneWidth
$$


投影矩阵：

![image-20190729193302240](http://ww4.sinaimg.cn/large/006tNc79gy1g5gw2piyg4j310w0owgu2.jpg)

判断点是否在视锥体内使用的不等式与透视矩阵相同。

### 屏幕空间(screen space)

这里把顶点从裁剪空间投影到屏幕空间中，来生成对应的2D坐标。

第一步：如果是透视投影，使用齐次除法(homogeneous division)，也被称为透视除法(perspective division)。用其次坐标系的w分量去除以x,y,z分量。OpenGL中把这一步得到的坐标叫做归一化的设备坐标(Normalized Device Coordinates, NDC)。这个立方体的x,y,z分量的范围都是$[0,-1]$ 。

正交投影的齐次除法不会对x,y,z产生影响。

经过齐次除法后，透视投影和正交投影的视锥体都变换到一个相同的立方体内。左下角的像素坐标(0,0)，右上角的像素坐标是(pixelWidth, pixelHeight)。这个映射的过程是一个缩放的过程。公式如下：

![image-20190729221232547](http://ww3.sinaimg.cn/large/006tNc79gy1g5h0oq05myj30pq07uacx.jpg)

z分量会被用于深度缓冲。

Unity中，Shader只需要把顶点转换到裁剪空间即可

总流程：

![image-20190729222000439](http://ww1.sinaimg.cn/large/006tNc79gy1g5h0wglbd8j31bw0u0ke1.jpg)

### 法线变换

游戏中，模型的一个顶点往往会携带额外的信息，而顶点法线就是其中一种信息。变换一个模型的时候，不仅需要变换它的顶点，还需要变换顶点法线，以便在后续处理中计算光照。

切线(tangent)，也被称为切矢量(tangent vector)。通常与纹理空间对齐，而且与法线垂直。

法线的变换需要顶点变换矩阵的逆转置矩阵$(M^{-1}_{A \rightarrow B})^{\mathrm{T}}$

如果变换矩阵是正交矩阵，那么可以使用变换顶点的变换矩阵来直接变换法线。

## Unity内置变量（数学篇）

内置变换矩阵

![image-20190729230012473](http://ww3.sinaimg.cn/large/006tNc79gy1g5h229w4xmj31f60n4h5e.jpg)



Unity内置的摄像机和屏幕参数

![image-20190729230938608](http://ww4.sinaimg.cn/large/006tNc79gy1g5h2c3i1gbj31190u01kx.jpg)

进行矩阵乘法的时候，参数的位置决定是按列矩阵还是行矩阵进行乘法。

mul(M,v) = mul(v, transpose(M))

Unity提供的内置矩阵都是按列存储的，所以通常使用右乘的方式进行乘法。

CG中元素的初始化和访问顺序是行优先。

#### Unity中的屏幕坐标：ComputeScreenPos/VPOS/WPOS

获取屏幕空间的坐标

![image-20190729232325806](http://ww2.sinaimg.cn/large/006tNc79gy1g5h2qfky4oj312o058dj1.jpg)

如果屏幕分辨率为400x300，那么x的范围就是[0.5, 400.5]，y的范围是[0.5, 300.5]。**像素坐标并不是整数值**。

另一种是使用**ComputeScreenPos**函数

![image-20190729232939736](http://ww4.sinaimg.cn/large/006tNc79gy1g5h2wwwv62j311c0kedou.jpg)

因为需要避免差值影响结果，对w的除法需要在片元处理器中进行。