WebGL入门(二):viewing变换及其数学基础

1,009 阅读7分钟

前言

变换在图形学中有两个:

  • 一种就是上一篇文章说的modeling变换。
  • 还有一种就是接下来说的viewing变换。

Viewing变换也包含两种:

  • 视图/摄像机变换
  • 投影变换

Viewing

视图变换

首先模型不能像“问题”,不能每个人看到的都不一样。所以要先定义好一个场景需要怎么展示。要回答这个问题就要思考一下现实中是怎么样拍一张照片。

  • 第一:找一个好地方,把大家或者物体摆好pose(这一步也就是模型变换)
  • 第二:找一个好的角度用放置好照相机,并让相机往某个方向看(这一步就是视图变换)
  • 最后:“茄子”(这一步就是投影变换) 在图形学中也是经过这三个步骤将图形展示到屏幕上的。

视图变换就是找角度摆照相机的过程。联想到现实中,就是如何摆放一个照相机?

  • 很明显位置是非常重要的,所以第一步就是定义好位置-position(用e\vec e表示)
  • 相机照向哪里也是决定成像的因数,第二步定义相机看向哪里-look-at(用g\vec g表示)
  • 还得定义相机的向上方向,因为如果相机旋转的话,拍出来肯定会有差异,所以定义好向上方向就能把相机固定住(用t\vec t表示)。 image.png

下一步就是如何去进行视图变换,首先根据初中的“相对运动”物理知识,只要相机与物体的距离,角度等都不发生变化,照出来的图片一定是一样的。不管相机与物体是否发生移动。所以为了让操作简化,大家约定俗成的就是相机永远放置在原点,y轴永远是向上方向,相机永远看向-z方向。进行变换的永远是物体 image.png

所以视图变换就是将照相机从当前点变换到原点的变换 image.png

  • 首先,将位置e\vec e移动的原点
  • 第二,将观察的方向g\vec g旋转到-z方向上
  • 第三,将向上方向t\vec t旋转到y方向上
  • g\vec g x t\vec t方向自然而然的就会旋转到x方向上 image.png

用上一篇文章中学到过的矩阵知识,把上述过程写成变换矩阵Mview=RviewTviewM_{view} = R_{view}T_{view}。注意顺序,先平移后旋转。

平移到原点,自然得是减去位置值(xe,ye,zex_e, y_e, z_e):TviewT_{view} = [100xe010ye001ze0001]\begin{bmatrix} {1}&{0}&{0}&{-x_{e}}\\ {0}&{1}&{0}&{-y_{e}}\\ {0}&{0}&{1}&{-z_{e}}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}

接下来就是旋转矩阵,将g\vec g旋转到-zt\vec t旋转到yg\vec g x t\vec t旋转到x。但是把任意轴旋转到规范轴,比如说到-z(0,0,-1)非常困难,但反过来写却相对容易:比如将x(1,0,0)旋转到g\vec g x t\vec t。也就是说正着旋转不好求,但可以求逆变换。
1、首先确定一下这个逆变换矩阵的形式Rview1R_{view}^{-1} = [abc0def0fhi00001]\begin{bmatrix} {a}&{b}&{c}&{0}\\ {d}&{e}&{f}&{0}\\ {f}&{h}&{i}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix},所以是要求这a-i的这几个变量。

2、第一步将x轴(1,0,0)旋转到g\vec g x t\vec t。在齐次坐标里,x轴向量表示为[1000]\begin{bmatrix} {1}\\ {0}\\ {0}\\ {0}\\ \end{bmatrix},最后一位是0。可以求出Rview1R_{view}^{-1} = [xgxtbc0ygxtef0zgxthi00001]\begin{bmatrix} {x_{\vec g x \vec t}}&{b}&{c}&{0}\\ {y_{\vec g x \vec t}}&{e}&{f}&{0}\\ {z_{\vec g x \vec t}}&{h}&{i}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}

3、第二步将y轴(0,1,0)旋转到t\vec t中,可以得出Rview1R_{view}^{-1} = [xgxtxtc0ygxtytf0zgxtzti00001]\begin{bmatrix} {x_{\vec g x \vec t}}&{x_{\vec t}}&{c}&{0}\\ {y_{\vec g x \vec t}}&{y_{\vec t}}&{f}&{0}\\ {z_{\vec g x \vec t}}&{z_{\vec t}}&{i}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}

4、第三步将z轴(0,0,1)旋转到-g\vec g,同理Rview1R_{view}^{-1} = [xgxtxtxg0ygxtytyg0zgxtztzg00001]\begin{bmatrix} {x_{\vec g x \vec t}}&{x_{\vec t}}&{x_{\vec {-g}}}&{0}\\ {y_{\vec g x \vec t}}&{y_{\vec t}}&{y_{\vec {-g}}}&{0}\\ {z_{\vec g x \vec t}}&{z_{\vec t}}&{z_{\vec {-g}}}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}

5、旋转矩阵是正交矩阵,所以求逆变换就是其转秩。Rview1R_{view}^{-1} -> RviewR_{view},得出RviewR_{view} = [xgxtygxtzgxt0xtytzt0xgygzg00001]\begin{bmatrix} {x_{\vec g x \vec t}}&{y_{\vec g x \vec t}}&{z_{\vec g x \vec t}}&{0}\\ {x_{\vec t}}&{y_{\vec t}}&{z_{\vec t}}&{0}\\ {x_{\vec {-g}}}&{y_{\vec {-g}}}&{z_{\vec {-g}}}&{0}\\ {0}&{0}&{0}&{1}\\ \end{bmatrix}

投影变换

人已经摆好pose,相机也已经准备就绪了。所以接下来就是拍照了!拍照就是将三维的物体变成二维的一张照片,这个过程就是投影。

投影变换有两个模式:

  • 正交投影
  • 透视投影 这两种投影的本质区别就是是否有“近大远小”的性质。有这种性质就是透视投影,否则就是正交投影。 image.png

如图所示:
左边为透视投影:认为摄像机放置在一个点上,从摄像机出发连出一个四棱锥,从四棱锥中某个深度到另外一个深度的这块区域,把这片区域所有东西都显示到近处的平面上。

右边为正交投影:假设摄像机在无限远的地方,形成区域的近处平面与远处平面是大小一致的。

正交投影

正交投影比较简单,只需要将区域中的物体压扁在近处的平面上就行了。 image.png 如图所示:

  • 将摄像机放置到原点,看向-Z方向,向上方向为y轴方向。
  • 对比正交投影来说,将物体压扁的操作只需把z轴坐标放弃掉就行
  • 然后将所有物体移动和缩放在[-1, 1]的矩形框中。

在图形学中,先定义一个立方体。立方体的左(l)(r)x轴上,上(t)(r)y轴上,远(f)(n)z轴上。然后将这个立方体通过平移和缩放变换到原点的[1,1]3[-1, 1]^3,这是一个正则、规范、标准的立方体。也就是说不管什么样的立方体,都能变换成这个标准的立方体。 image.png

仔细看远和近,上面说到摄像机是看向-z方向的。所以说一个面离得远,z值是更小的。相反一个面离的近说明z值反而是更大的。所以说f是小于n的。

用矩阵的方式将上述变换写出来。
1、首先将这个立方体的中心点平移到原点。中心点也就是左和右的中心r+l2\frac{r+l}{2},上和下的中心t+b2\frac{t+b}{2},远和近的中心n+f2\frac{n+f}{2}。所以能得出MtM_t = [100r+l2010t+b2001n+f20001]\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}

2、然后通过scale变换将立方体缩放为[1,1]3[-1, 1]^3,也就是把这个立方体的边长都变成2,因为[-1,1]。左和右2rl\frac{2}{r-l},上和下2tb\frac{2}{t-b},远和近2nf\frac{2}{n-f}。所以是MsM_s = [2rl00002tb00002nf00001]\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}

3、最后得出Mortho=MsMtM_ortho = M_s M_t

透视投影

透视投影是图形学中用的最广泛的投影,满足“近大远小”这个性质的投影就是透视投影。

一般认为把摄像机放置在一个点上,从摄像机出发连出一个四棱锥,从四棱锥中某个深度到另外一个深度的这块区域,把这片区域所有东西都显示到近处的平面上就叫做透视投影。 image.png 与正交投影一样,也定义近与远(nf)两个面。如图所示,透视投影也就是将远处面上的点变换在近面上的点。通过将两点连线可以直观的看出点变换的规律。可以将透视投影分为两个步骤:

  • 首先,将远面通过缩放变换将其变换与近面一样大小的面,将透视投影变为正交投影
  • 第二,进行正交投影 image.png

从侧面看 image.png 左边点就是相机摆放位置,向上方向是y,看向-z方向。直观的看出yy^{'} = nz\frac{n}{z}y,因为相似三角形的性质。

同理求得xx^{'} = nz\frac{n}{z}x,剩下zz还不清楚。那么写成齐次坐标的形式[xyz1]\begin{bmatrix} {x}\\ {y}\\ {z}\\ {1}\\ \end{bmatrix} = [nx/zny/z?1]\begin{bmatrix} {nx/z}\\ {ny/z}\\ {?}\\ {1}\\ \end{bmatrix}

回顾一下上一篇文章的内容,在齐次坐标中[xyz1]\begin{bmatrix} {x}\\ {y}\\ {z}\\ {1}\\ \end{bmatrix}表示的是空间中的一个点,然后将其中的x,y,z,1都乘上一个相同的数k(k不等于0)仍然表示相同的点。那么将[nx/zny/z?1]\begin{bmatrix} {nx/z}\\ {ny/z}\\ {?}\\ {1}\\ \end{bmatrix}乘上z得到的仍然是相同的点[nxny?z]\begin{bmatrix} {nx}\\ {ny}\\ {?}\\ {z}\\ \end{bmatrix}。根据这个性质,我们可以推倒出变换矩阵Mpersp>orthoM_{persp->ortho} = [n0000n00????0010]\begin{bmatrix} {n}&{0}&{0}&{0}\\ {0}&{n}&{0}&{0}\\ {?}&{?}&{?}&{?}\\ {0}&{0}&{1}&{0}\\ \end{bmatrix}

那么z到底是怎么变换?上面说透视投影是将远面挤压成和近面一样的大小,所以能得出两个结论

  • 在近面上的所有点经过变换后都不会变换
  • 在远面上点中的z经过变换后也不会发生变换 根据第一点性质近面上的点经过变换后[xyn1]\begin{bmatrix} {x}\\ {y}\\ {n}\\ {1}\\ \end{bmatrix}(为啥是n?因为近面上z轴的值为n) = [nxnyn2n]\begin{bmatrix} {nx}\\ {ny}\\ {n^{2}}\\ {n}\\ \end{bmatrix},那么矩阵的第三行一定是(0 0 A B)。也就是说(0 0 A B)[xyn1]\begin{bmatrix} {x}\\ {y}\\ {n}\\ {1}\\ \end{bmatrix} = n2n^2 => An + B = n2n^2

根据第二个性质,取远面的中心点z,也就是[00z1]\begin{bmatrix} {0}\\ {0}\\ {z}\\ {1}\\ \end{bmatrix},经过变换后还是一样的点,表示为[00f2f]\begin{bmatrix} {0}\\ {0}\\ {f^{2}}\\ {f}\\ \end{bmatrix} => Af + B = f2f^2

结合这两个展开式

An+B=n2Af+B=f2An + B = n^2\\ Af + B = f^2\\

可以求到A、B的值

A=n+fB=nfA = n + f\\ B = -nf\\

最终得出Mpersp>orthoM_{persp->ortho} = [n0000n0000n+fnf0010]\begin{bmatrix} {n}&{0}&{0}&{0}\\ {0}&{n}&{0}&{0}\\ {0}&{0}&{n+f}&{-nf}\\ {0}&{0}&{1}&{0}\\ \end{bmatrix}

然后透视投影的矩阵Mpersp=MorthoMpersp>orthoM_{persp} = M_{ortho}M_{persp->ortho}

到这里viewing变换已经讲清楚了!

结尾

如果觉得有帮助的请点点赞,支持一下,一起学习webgl吧!

webgl专栏

更多文章请移步楼主github,如果喜欢请点一下star,对作者也是一种鼓励。