图形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 $$
$$ [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的投影矩阵结果是一样的。
那么造成差别的是在哪一步呢?
首先来看正交投影部分
$$ 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} $$
$$ 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