【笔记】【百人计划】图形1.2.3 MVP矩阵运算

图形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)

image-20220721144144034

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的方向来观察。

我们绕哪个轴旋转,哪个轴上的坐标就不变,这很明显,因此只需要观察剩下的两个轴所构成的平面。

image-20220721180506988

如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变换顺序

image-20220721163348382 image-20220721163401296

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矩阵。

变换过程:将摄像机和世界坐标重合(相机在世界空间的变换是先旋转,再平移)

  1. 平移
  2. 旋转
  3. (反转左右手坐标系:z分量取反)

首先定义一个相机

  • 位置
  • 观察方向Look-at/gaze direction
  • 向上方向Up direction

让相机和物体保持相对位置,一起移动——与世界坐标重合,再旋转

这里的重合,我们需要让g(gaze)和Y(深度)轴重合,t(up)和Y重合,另一个方向和x轴重合。

image-20220721190933162

对于相机位置$(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:相机空间->齐次裁剪空间

  1. 不是真正的投影,为投影做准备
  2. 目的:判断定点是否在可见范围内
  3. 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在这里的讲述更复杂也更通用的一步,是定义了视锥体和观察坐标系之间的关系

image-20220721200902778

视锥近平面中心不在观察中心。目前我还没接触到过使用这种特性。但其实都是一样的

这样的话,我们需要先平移再缩放,很明显
$$
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挤压成长方体,然后进行正交投影

image-20220721232039529

$M_{persp} = M_{ortho}M_{persp\to ortho}$

对于Frustum中间一点(x,y,z)

image-20220721232239730

$$
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}$,

image-20220722005643105

$$
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

[5] https://zhuanlan.zhihu.com/p/463027517