图形1.2MVP矩阵运算
MVP矩阵
- Model模型,View观察,Projection投影矩阵
空间
- Local Space(Local Coordinate)
- World Space(World Coordinate)
- View/Camera Space(View/Camera Coordinate)
- Clip Space(Clip Coordinate)
- Screen Space(Screen Coordinate)
M:模型空间->世界空间
对于模型本身来说,有自身的Local Space,当把模型放入世界空间场景去描述时,就需要把模型的局部坐标变换到世界坐标,这个变换用矩阵描述,就是Model矩阵
$M_{Model} = M_TM_RM_S$
平移Translation
Translation变换不是线性变换
$$
\begin{bmatrix}x’\y’ \end{bmatrix} =\begin{bmatrix}a&b\c&d \end{bmatrix} \begin{bmatrix}x\y \end{bmatrix}+\begin{bmatrix}t_x\t_y \end{bmatrix}
$$
齐次坐标
$2D point = (x,y,1)^T$
$2Dvector = (x,y,0)^T$ (向量的平移不变性)
$$
\begin{bmatrix}{}x’\y’\1’ \end{bmatrix}{} = \begin{bmatrix}{}1&0&t_x\0&1&t_y\0&0&1 \end{bmatrix}{}\cdot\begin{bmatrix}{}x\y\1 \end{bmatrix}{} = \begin{bmatrix}{}x+t_x\y+t_y\1 \end{bmatrix}{}
$$
对于点 $(x,y,w)^T$ 的表达,与 $(x/w,y/w,1)^T,w\neq 0$ 等价
仿射变换
对于任何一种变换能够写成
Affine map = linear map + translation
$$
\begin{bmatrix}x’\y’ \end{bmatrix} =\begin{bmatrix}a&b\c&d \end{bmatrix} \begin{bmatrix}x\y \end{bmatrix}+\begin{bmatrix}t_x\t_y \end{bmatrix}
$$
表示仿射变换,使用齐次坐标可以表示成矩阵乘法
$$
\begin{bmatrix}{}x’\y’\1’ \end{bmatrix}{} = \begin{bmatrix}{}a&b&t_x\c&d&t_y\0&0&1 \end{bmatrix}{}\cdot\begin{bmatrix}{}x\y\1 \end{bmatrix}{}
$$
3维的平移矩阵
$$
T(t_x,t_y,t_z) = \begin{bmatrix}1&0&0&t_x\0&1&0&t_y\0&0&1&t_z\0&0&0&1\end{bmatrix}
$$
旋转Rotation
$$
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},\
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},\
R_z(\theta) =\begin{bmatrix}\cos\theta&0&-\sin\theta&0
\\sin\theta&0&\cos\theta&0
\0&0&1&0
\0&0&0&1
\end{bmatrix}
$$
旋转矩阵是正交的
接下来我们来说一说为什么绕y轴的旋转矩阵长得有点不一样。
这里是同一个右手坐标系,分别从x,z,y的方向来观察。
我们绕哪个轴旋转,哪个轴上的坐标就不变,这很明显,因此只需要观察剩下的两个轴所构成的平面。
如ZOY,YOX平面,在右手坐标系下,和二维的YOX旋转是一样的。从Y到Z,从X到Y的顺序是一致的。
但是在以Y轴作为旋转轴的XOZ平面上,是相反的,因此旋转的角度也变成了相反数。
任意旋转
对于任意一种旋转状态
$$
R_{xyz}(\alpha,\beta,\gamma)=R_x(\alpha)R_y(\beta)R_z(\gamma)
$$
这就也就是欧拉角
Rodrigues’ Rotation Formula罗德里格斯旋转公式
给定任意旋转轴和旋转角度转化为对应的旋转矩阵。
$$
R(n,\alpha) = \cos(\alpha)I+(1-\cos(\alpha))nn^T+\sin(\alpha)\begin{bmatrix}0&-n_z&n_y\n_z&0&-n_x\-n_y&n_x&0\end{bmatrix}
$$
如果想绕任意起点的轴,则与2D一样,先平移至原点,绕原点轴旋转,再平移回去。
四元数
放一放吧
但是可以先放个参考资料
quaternion.pdf (krasjet.github.io)
缩放Scale
$$
S =\begin{bmatrix}s_x&0&0&0
\0&s_y&0&0
\0&0&s_z&0
\0&0&0&1
\end{bmatrix}
$$
Model变换顺序


3维的仿射变换齐次坐标表示
$$
\begin{bmatrix}x’\y’\z’\1 \end{bmatrix} =
\begin{bmatrix}a&b&c&t_x
\d&e&f&t_y
\g&h&i&t_z
\0&0&0&1 \end{bmatrix} \cdot\begin{bmatrix}x\y\z\1\end{bmatrix}
$$
对应的Affine map = linear map + translation,
$$
\begin{bmatrix}x’\y’ \end{bmatrix} =\begin{bmatrix}a&b\c&d \end{bmatrix} \begin{bmatrix}x\y \end{bmatrix}+\begin{bmatrix}t_x\t_y \end{bmatrix}
$$
translation的顺序是在最后的,因此一定要先进行线性变换,再进行平移
因此
$$
M_{model} =M_{T}M_{R}M_{S}=
\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}a&b&c&0
\d&e&f&0
\g&h&i&0
\0&0&0&1
\end{bmatrix}
\begin{bmatrix}s_x&0&0&0
\0&s_y&0&0
\0&0&s_z&0
\0&0&0&1
\end{bmatrix}
$$
其中旋转矩阵是任意旋转矩阵的组合
V:世界空间->相机空间
经过Model矩阵的变换后,物体从模型空间变换到了世界空间的某个状态。在世界空间里的物体被人眼/相机看到,相机要渲染出这张图像,就要以相机为中心来描述,那么就需要把世界空间的模型再变换到相机空间/观察空间。这就是View矩阵。
变换过程:将摄像机和世界坐标重合(相机在世界空间的变换是先旋转,再平移)
- 平移
- 旋转
- (反转左右手坐标系:z分量取反)
首先定义一个相机
- 位置
- 观察方向Look-at/gaze direction
- 向上方向Up direction
让相机和物体保持相对位置,一起移动——与世界坐标重合,再旋转
这里的重合,我们需要让g(gaze)和Y(深度)轴重合,t(up)和Y重合,另一个方向和x轴重合。

对于相机位置$(x_e,y_e,z_e)$
$$
M_{view}=R_{view}T_{view}\
T_{view}=\begin{bmatrix}1&0&0&-x_e\
0&1&0&-y_e\
0&0&1&-z_e\
0&0&0&1
\end{bmatrix}
$$
而该如何旋转?直接求是不好求的,但是根据相机的坐标系我们可以知道
$$
R^{-1}{view}=\begin{bmatrix}
x{g\times t} &x_t&x_{-g}&0
\y_{g\times t}&y_t&y_{-g}&0
\z_{g\times t}&z_t&z_{-g}&0
\0&0&0&1
\end{bmatrix}
\
R_{view}=(R^{-1}{view})^T=\begin{bmatrix}
x{g\times t}&y_{g\times t} &z_{g\times t}&0
\x_t&y_t&z_t&0
\x_{-g}&y_{-g}&z_{-g}&0
\0&0&0&1
\end{bmatrix}
\
$$
而这里对于g轴和z轴的方向统一问题,也就是左右手坐标系统一的问题,从上图可以看到,世界坐标仍然是右手系,但是相机空间定义的是左手系,指向世界空间z轴负方向。
Unity Shader入门精要中,世界空间是左手系,观察空间是右手系(Opengl)给出的解决方法是,最后再乘上一个negate z矩阵
$$
M_{view}=M_{negate\ z}M_{view}\
M_{negate\ z}=\begin{bmatrix}1&0&0&0
\0&1&0&0
\0&0&-1&0
\0&0&0&1
\end{bmatrix}
$$
就是其实就是把z坐标取反,这也是101作业当中的处理方法,其实也就是上面为什么写-g。这种细节问题,使用引擎什么的一般不会用到,但是在自己写引擎的时候,就是必须需要注意的细节了。
P:相机空间->齐次裁剪空间
- 不是真正的投影,为投影做准备
- 目的:判断定点是否在可见范围内
- P矩阵:对xyz分量进行缩放,用w分量做范围值,如果x,y,z都在w范围内,那么该点在裁剪空间内
- 投影类型
- 透视投影
- 正交投影
其实在这次回顾101的时候发现,闫老师早就把View Transformation和Projection Transformation放到一起,统称为Viewing(观测)Transformation(这是很有道理的,因为这个阶段并没有进行透视除法,还在三维空间当中,透视除法才是把三维“投影“到二维当中的过程)。
投影矩阵只是把View frustum(视锥体)中的六面体空间变换为一个单位立方体空间(Canonical Cube) $[-1,1]^3$。
- Viewing(观测) transformation
- View/Camera(视图) transformation
- Projection(投影) transformation
- Orthographic projection
- Perspective projection
正交投影Orthographic Projection
正交投影中的视锥变换定义$[l,r]\times[b,t]\times[f,n]->[-1,1]^3$
正交投影的变换形式可想而知,在x和y轴上需要进行缩放,在z轴上除了缩放还要进行一定的平移,使立方体中心移动到坐标原点。
101在这里的讲述更复杂也更通用的一步,是定义了视锥体和观察坐标系之间的关系
视锥近平面中心不在观察中心。目前我还没接触到过使用这种特性。但其实都是一样的
这样的话,我们需要先平移再缩放,很明显
$$
M_{ortho}=\begin{bmatrix}
\frac{2}{r-l}&0&0&0
\0&\frac{2}{t-b}&0&0
\0&0&\frac{2}{n-f}&0
\0&0&0&1
\end{bmatrix}
\begin{bmatrix}
1&0&0&-\frac{r+l}{2}
\0&1&0&-\frac{t+b}{2}
\0&0&1&-\frac{n+f}{2}
\0&0&0&1
\end{bmatrix}=\begin{bmatrix}
\frac{2}{r-l}&0&0&\frac{r+l}{l-r}
\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}
\0&0&\frac{2}{n-f}&\frac{n+f}{f-n}
\0&0&0&1
\end{bmatrix}
$$
那其实我们令相机在视锥体近平面中心轴线上,t=-b=size,r=-l =Aspect*size
$$
M_{ortho}=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&0
\0&0&0&1
\end{bmatrix}\begin{bmatrix}
1&0&0&0
\0&1&0&0
\0&0&1&-\frac{n+f}{2}
\0&0&0&1
\end{bmatrix}\=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}
$$
也就是入门精要里的结果
透视投影Perspective Projection
思路是先将透视投影的Frustum挤压成长方体,然后进行正交投影

$M_{persp} = M_{ortho}M_{persp\to ortho}$
对于Frustum中间一点(x,y,z)

$$
M_{p\to o}\begin{bmatrix}x\y\z\1\end{bmatrix}=\begin{bmatrix}nx/z\ny/z\unknown\1\end{bmatrix}=>\begin{bmatrix}nx\ny\unknown\z\end{bmatrix}\
M_{p\to o}=\begin{bmatrix}n&0&0&0\0&n&0&0\?&?&?&?\0&0&1&0\end{bmatrix}
$$
考虑
- 近平面的点不会改变
$$
M_{p\to o}\begin{bmatrix}x\y\n\1\end{bmatrix}=
\begin{bmatrix}nx/n\ny/n\n\1\end{bmatrix}=>\begin{bmatrix}nx\ny\n^2\n\end{bmatrix}\
$$
对于第三行的计算
$$
[a,b,c,d][x,y,n,1]^T = n^2\a=b=0
\cn+d=n^2
$$
- 远平面点的z值不变
$$
[0,0,c,d][0,0,f,1]^T=f^2
\cf+d = f^2
$$
可以解得
$$
c=n+f\d=-nf
$$
即
$$
M_{p\to o}=\begin{bmatrix}n&0&0&0\0&n&0&0\0&0&n+f&-nf\0&0&1&0\end{bmatrix}
\
M_p=\begin{bmatrix}
\frac{2}{r-l}&0&0&\frac{r+l}{l-r}
\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}
\0&0&\frac{2}{n-f}&\frac{n+f}{f-n}
\0&0&0&1
\end{bmatrix}\begin{bmatrix}n&0&0&0\0&n&0&0\0&0&n+f&-nf\0&0&1&0\end{bmatrix}
=\begin{bmatrix}
\frac{2n}{r-l}&0&\frac{r+l}{l-r}&0
\0&\frac{2n}{t-b}&\frac{t+b}{b-t}&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}
$$
这个结果也正如虎书上的结果。
对比与思考
但是这个时候问题来了,这个结果和Unity Shader入门精要上不一样……和一些其他地方的结果也不太一样……
最大的问题,都是出在四行三列和三行四列的符号上。
我们先继续考虑 $t=-b=size,r=-l =Aspect*size,\tan\frac{\theta}{2}=\frac{t}{n}$,

$$
M_p=\begin{bmatrix}
\frac{2n}{r-l}&0&\frac{r+l}{l-r}&0
\0&\frac{2n}{t-b}&\frac{t+b}{b-t}&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}=\begin{bmatrix}
\frac{n}{r}&0&0&0
\0&\frac{n}{t}&0&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}\=\begin{bmatrix}
-\frac{\cot\frac{\theta}{2}}{Aspect}&0&0&0
\0&-\cot\frac{\theta}{2}&0&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}
$$
入门精要里和其它一些地方的结果是:
$$
\begin{bmatrix}
\frac{\cot\frac{\theta}{2}}{Aspect}&0&0&0
\0&\cot\frac{\theta}{2}&0&0
\0&0&\frac{near+far}{near-far}&\frac{2nearfar}{near-far}
\0&0&-1&0
\end{bmatrix}
$$
事实上,这和虎书后面给出的Opengl的投影矩阵结果是一样的。
那么造成差别的是在哪一步呢?
首先来看正交投影部分
- Result1
$$
M_{ortho}=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}
$$
- Result2
$$
M_{ortho}=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{near-far}&-\frac{near+far}{far-near}
\0&0&0&1
\end{bmatrix}
$$
一个比较坑的点是near/far表示正值,n/f表示坐标(负值),但是这不影响符号的相对差别。
即便把near/far反过来,第三行整体就是反的了。
后来发现,unity/opengl中希望这个frustum的近平面能够对应到立方体的-1,远平面对应到立方体的1。
但是虎书推到和闫老师的平移做法当中,是直接平移中点然后缩放,这就导致即便都是立方体,但是这里近平面对应的是1,远平面对应-1
所以这里就出现了矛盾。
给第一个结果做一个z值矫正
$$
M_{ortho}=\begin{bmatrix}
1&0&0&0
\0&1&0&0
\0&0&-1&0
\0&0&0&1
\end{bmatrix}\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}\=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{f-n}&\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}
$$
再考虑n和f的绝对值,就能得到unity/opengl的正交投影结果了
我们带着这个结果往下看
$$
M_p=\begin{bmatrix}
\frac{2}{r-l}&0&0&\frac{r+l}{l-r}
\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}
\0&0&\frac{2}{f-n}&\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}\begin{bmatrix}n&0&0&0\0&n&0&0\0&0&n+f&-nf\0&0&1&0\end{bmatrix}
=\begin{bmatrix}
\frac{2n}{r-l}&0&\frac{r+l}{l-r}&0
\0&\frac{2n}{t-b}&\frac{t+b}{b-t}&0
\0&0&\frac{n+f}{f-n}&\frac{2nf}{n-f}
\0&0&1&0
\end{bmatrix}
$$
和这篇文章的结果是一样的https://zhuanlan.zhihu.com/p/463027517
$M_{p\to o}$计算中考虑n和f的绝对值也就是Unity/opengl的结果。
$$
M_{p\to o}\begin{bmatrix}x\y\z\1\end{bmatrix}=\begin{bmatrix}-|n|x/z\-|n|y/z\unknown\1\end{bmatrix}=>\begin{bmatrix}|n|x\|n|y\unknown\-z\end{bmatrix}\
M_{p\to o}=\begin{bmatrix}|n|&0&0&0\0&|n|&0&0\?&?&?&?\0&0&-1&0\end{bmatrix}
$$
这样就构造出了-1
然后后面的计算就自然而然了。
$$
\footnotesize{M_{p\to o}\begin{bmatrix}x\y\z\1\end{bmatrix}=\begin{bmatrix}-|n|x/z\-|n|y/z\unknown\1\end{bmatrix}=>\begin{bmatrix}|n|x\|n|y\unknown\-z\end{bmatrix}}\
M_{p\to o}=\begin{bmatrix}|n|&0&0&0\0&|n|&0&0\0&0&|n|+|f|&|n||f|\0&0&-1&0\end{bmatrix}\
\footnotesize{M_p=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{near-far}&\frac{near+far}{near-far}
\0&0&0&1
\end{bmatrix}\begin{bmatrix}near&0&0&0\0&near&0&0\0&0&near+far&nearfar\0&0&-1&0\end{bmatrix}}
\\small{=\begin{bmatrix}
\frac{\cot\frac{\theta}{2}}{Aspect}&0&0&0
\0&\cot\frac{\theta}{2}&0&0
\0&0&\frac{near+far}{near-far}&\frac{2nearfar}{near-far}
\0&0&-1&0
\end{bmatrix}}
$$
终于和入门精要的结果一致了。。。
总结一下,影响这些透视投影矩阵符号的两个因素
- near/far是正值,n/f是坐标
- 一部分将frustum近平面映射到-1,远平面映射到1;一部分则相反
参考资料
[1]https://www.bilibili.com/video/BV1M5411P7d3 【技术美术百人计划】图形 1.2.3 MVP矩阵运算
[2]https://www.bilibili.com/video/BV1X7411F744 GAMES101-现代计算机图形学入门-闫令琪
[3] Unity Shader 入门精要
[4] Fundamentals of Computer Graphics,4th