Shader学习之路(三)
Shader学习之路(三)
学习Shader之前先来复习复习线性代数的一些知识吧 可能分两次,内容有点多。
第一节 坐标系
一.笛卡尔坐标系
这个我就省略了,只要有上过高中的都知道,或者说前段时间《隐秘的角落》不是很火吗,里面就有提到笛卡尔。
二.左手坐标系和右手坐标系
如果两个做个坐标系具有旋向性,那么我们就可以哦通过旋转的方式来让他们的坐标轴重合,但是比如左手坐标系和右手坐标系就没有。
- 还有一种判断方法就是高中物理类似的左手和右手法则。
- 使用左手如果x叉乘y为z,那么就是左手坐标系
- 同理使用右手如果x叉乘y为z,那么就是右手坐标系
- 其实具体使用的时候没什么差别,不过一旦使用就得用这个坐标系建立到底
三.Unity使用的坐标系
- 在Scene视图(模型空间)是左手坐标系
- 对于观察空间来说是右手坐标系,观察空间通俗来讲就是以摄像机为原点的坐标系
第二节 点和矢量
- 矢量的乘法/除法
- 矢量的加法减法
- 三角形法则
- 矢量的模
- 单位矢量:长度为1的矢量
- 0矢量
- 矢量的点积(內积或者投影)
- 矢量的叉积(外积)
\[b=(a_x,a_y,a_z) \times (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)\]
或者
\[\begin{bmatrix}x & y & z \\a_x & a_y & a_z \\ b_x & b_y & b_z\end{bmatrix}=(a_y*b_z-a_z*b_y,a_z*b_x-a_x*b_z,a_x*b_y-a_y*b_x) \]
第三节 矩阵
行矩阵
列矩阵
矩阵运算
矩阵乘法
A的第二行和B的第三列分别相乘后之和就是c23
即
通式:
对角矩阵:
单位矩阵
性质:
转置矩阵:
性质:
逆矩阵:
性质一:并不是所有的矩阵都有逆矩阵
性质二:单位矩阵的逆矩阵是它本身.即
性质三:转置矩阵的逆矩阵是逆矩阵的转置.即
性质四:矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵.即
下面是使用M
矩阵对v
矢量进行一次变化又用逆矩阵变了回去
正交矩阵:
矩阵M是正交的等价于:
矩阵M是正交的等价于:
下面举一个栗子区分一下行矩阵和列矩阵
v是行矩阵
v是列矩阵
在Unity中通常是吧矢量放在矩阵的右侧,即把矢量转化成列矩阵来运算
第四节 矩阵的几何意义:变换
1.通过使用矩阵运算,能将一个矢量进行任意的平移或旋转(线性变换)
线性变换:判断依据
由于考虑平行变换例如:
他不是一个线性变换,所以我们不能用3*3
的矩阵来表示平移变换。因此,就有了仿射变换,仿射变换可以使用一个4*4
的矩阵表示,于是我们把矢量拓展到四维空间就是齐次坐标空间.
下表仅仅作为参考,不需要记
重点
2.齐次坐标
即4*4
矩阵(本内容介绍定义为),可以用来表示平移,旋转和缩放。
我们把纯平移,纯旋转和纯缩放的变换矩阵叫做基础变换矩阵,他们有共同点
-
平移矩阵
有平移效果(点平移)
\[\begin{bmatrix}1 & 0 & 0 & t_x\\0 & 1 & 0 & t_y\\ 0 & 0 & 1 & t_z\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}x \\ y \\ z \\ 1 \end{bmatrix}=\begin{bmatrix}x+t_x \\ y+t_y \\ z+t_z \\ 1 \end{bmatrix} \]无平移效果(矢量平移:矢量本来就是怎么平移都是一样的,没有位置属性)
\[\begin{bmatrix}1 & 0 & 0 & t_x\\0 & 1 & 0 & t_y\\ 0 & 0 & 1 & t_z\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}x \\ y \\ z \\ 0 \end{bmatrix}=\begin{bmatrix}x \\ y \\ z \\ 0 \end{bmatrix} \]下面是平移矩阵的逆矩阵,至于怎么得到逆矩阵的,可以上网查查如何得到逆矩阵,大学线性代数都有教。
平移矩阵的逆矩阵就是反向平移得到的矩阵,即\[\begin{bmatrix}1 & 0 & 0 & -t_x\\0 & 1 & 0 & -t_y\\ 0 & 0 & 1 & -t_z\\ 0 & 0 & 0 & 1\end{bmatrix} \] -
缩放矩阵
对点缩放
\[\begin{bmatrix}k_x & 0 & 0 & 0\\0 & k_y & 0 & 0\\ 0 & 0 & k_z & 0\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}x \\ y \\ z \\ 1 \end{bmatrix}=\begin{bmatrix}k_xx \\ k_yy \\ k_zz \\ 1 \end{bmatrix} \]对矢量缩放
\[\begin{bmatrix}k_x & 0 & 0 & 0\\0 & k_y & 0 & 0\\ 0 & 0 & k_z & 0\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}x \\ y \\ z \\ 0 \end{bmatrix}=\begin{bmatrix}k_xx \\ k_yy \\ k_zz \\ 0 \end{bmatrix} \]\[如果k_1=k_2=k_3那么为同一缩放,否则为非同一缩放,一般来说使用同一缩放 \]缩放矩阵的逆矩阵是使用原缩放系数的倒数来对点或方向矢量进行缩放,即
\[\begin{bmatrix}\frac{1}{k_x} & 0 & 0 & 0\\0 & \frac{1}{k_y} & 0 & 0\\ 0 & 0 & \frac{1}{k_z} & 0\\ 0 & 0 & 0 & 1\end{bmatrix} \]上面的矩阵只适合沿坐标轴方向进行缩放。如果我们希望任意方向缩放,就要用到复合变换,一种方法是:现将缩放轴变换成标准坐标轴,然后进行沿坐标轴的缩放,再使用逆变换得到原来的缩放轴朝向。
-
旋转矩阵
绕x轴旋转
\[R_x(\theta)=\begin{bmatrix}1 & 0 & 0 & 0\\0 & \cos\theta & -\sin\theta & 0\\ 0 & \sin\theta & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix} \]即
\[\begin{bmatrix}x' \\ y' \\ z' \\ 1 \end{bmatrix} =\begin{bmatrix}1 & 0 & 0 & 0\\0 & \cos\theta & -\sin\theta & 0\\ 0 & \sin\theta & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix}*\begin{bmatrix}x \\ y \\ z \\ 1 \end{bmatrix} \]绕y轴旋转
\[R_y(\theta)=\begin{bmatrix}\cos\theta & 0 & \sin\theta & 0\\0 & 1 & 0 & 0\\ -\sin\theta & 0 & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix} \]即
\[\begin{bmatrix}x' \\ y' \\ z' \\ 1 \end{bmatrix} =\begin{bmatrix}\cos\theta & 0 & \sin\theta & 0\\0 & 1 & 0 & 0\\ -\sin\theta & 0 & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix}*\begin{bmatrix}x \\ y \\ z \\ 1 \end{bmatrix} \]\[\begin{bmatrix}z' \\ y' \\ x' \\ 1 \end{bmatrix} =\begin{bmatrix}\cos\theta & 0 & -\sin\theta & 0\\0 & 1 & 0 & 0\\ \sin\theta & 0 & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix}*\begin{bmatrix}z \\ y \\ x \\ 1 \end{bmatrix} \]绕z轴旋转
\[R_z(\theta)=\begin{bmatrix}\cos\theta & -\sin\theta & 0 & 0\\\sin\theta & \cos\theta & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\end{bmatrix} \]\[\begin{bmatrix}x' \\ y' \\ z' \\ 1 \end{bmatrix} =\begin{bmatrix}\cos\theta & -\sin\theta & 0 & 0\\\sin\theta & \cos\theta & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\end{bmatrix}*\begin{bmatrix}x \\ y \\ z \\ 1 \end{bmatrix} \]是正交矩阵,多个旋转矩阵之间的串联同样正交
-
复合变换
绝大多数数情况下,我们约定变换的顺序就是先缩放,再旋转,最后平移
否则最后得到的结果可能会不一样,即\[M_{translation}M_{rotation}M_{scale}=\begin{bmatrix}1 & 0 & 0 & t_x\\0 & 1 & 0 & t_y\\ 0 & 0 & 1 & t_z\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}\cos\theta & 0 & \sin\theta & 0\\0 & 1 & 0 & 0\\ -\sin\theta & 0 & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}k_x & 0 & 0 & 0\\0 & k_y & 0 & 0\\ 0 & 0 & k_z & 0\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}x \\ y \\ z \\ 1 \end{bmatrix}=\begin{bmatrix}k_x\cos\theta & 0 & k_z\sin\theta & t_x\\0 & k_y & 0 & t_y\\ -k_x\sin\theta & 0 & k_z\cos\theta & t_z\\ 0 & 0 & 0 & 1\end{bmatrix}\neq M_{rotation}M_{scale}M_{translation} \]上面别记了,记不牢的
其次,旋转的顺序是不是也得明白一下,在Unity官方文档的旋转顺序是zxy(为什么?物体旋转从里到外为z y x,外层带动内层,使用zxy可以保证世界旋转轴不动(外旋是左乘))(Golbal模式下调整的时候)这是按照第一种方式理解.\[假设要使物体旋转({\theta}_x,{\theta}_y,{\theta}_z) \]我们对于旋转的理解可以分两种
- 方式来的
- 如果我们想要转换成按照自身坐标系的方法的话(因为矩阵变换的时候坐标轴会随着物体旋转而变化)
所以我们应该使用yxz的方式(Local模式下调整的时候),即反过来就是一样的了。而使用旋转变化矩阵的时候就是(内旋是右乘)
\[M_{rotate{\theta}_Z}M_{rotate{\theta}_X}M_{rotate{\theta}_Y}=\begin{bmatrix}\cos\theta & -\sin\theta & 0 & 0\\\sin\theta & \cos\theta & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}1 & 0 & 0 & 0\\0 & \cos\theta & -\sin\theta & 0\\ 0 & \sin\theta & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}\cos\theta & 0 & \sin\theta & 0\\0 & 1 & 0 & 0\\ -\sin\theta & 0 & \cos\theta & 0\\ 0 & 0 & 0 & 1\end{bmatrix} \]也就是说
transform.Rotate(new Vector3(30, 90, -40));
和
transform.Rotate(new Vector3(0, 90, 0)); transform.Rotate(new Vector3(30, 0, 0)); transform.Rotate(new Vector3(0, 0, -40));
结果一样,进一步说明前者是
第一种旋转
,后者是第二种旋转
PS:
- 每次旋转是绕固定轴(一个固定参考系,比如世界坐标系)旋转,称为外旋。
- 每次旋转是绕自身旋转之后的轴旋转,称为内旋。
参考链接:欧拉角和旋转矩阵之间的转换
参考书籍:《Shader入门精要》(冯乐乐)