课程链接:GAMES101-现代计算机图形学入门-闫令琪
课程讲师:闫令琪
本系列笔记为本人根据学习该门课程的笔记,仅分享出来供大家交流,希望大家多多支持GAMES相关讲座及课程,如涉及侵权请联系我删除:albertlidesign@gmail.com
二维变换
Scale
在图形学中Scale变换是非常简单的,如果你想把一个物体Scale至它的倍,那么只需要将这个物体上所有点的分量都乘以,写成矩阵形式就是
Reflection
将一个物体的镜像也很简单,在二维中,这个物体想沿着哪个轴镜像就将另一个轴的分量乘以即可,即
Shear
在做切变的时候要注意,假设平面上左下角过原点的边长为的正方形上方的两个点沿轴平移个单位,那么我们会发现:
(1) 上的点没有移动
(2) 上的点移动了个单位,如点移动至了
(3) 垂直方向没有移动
(4) 如果假设上有一个点,那么它应该移动至
那么也就是说,实际上所有的都没变,而所有的都变为
因此用矩阵表示如下:
Rotate
首先规定,任何时候我们说旋转都是默认绕着原点进行旋转(绕其他点旋转可以看作是先将物体移动至原点,进行旋转操作后再移动回去),另外,如果不规定旋转方向,那么我们默认都是逆时针旋转。现有一个物体,假设让它旋转度,那么可以根据三角函数来求出旋转后的对应点的坐标。例如点将会旋转至点,点将会旋转至点。
这样我们就能很轻易地写成矩阵形式:
那么如果向顺时针方向旋转该怎么表达呢?顺时针方向旋转其实就是旋转了,将其代如旋转矩阵得到
这里我们发现刚好,并且,旋转角和旋转角正好是互逆的操作,因此还有(逆变换的意义其实就是将矩阵变换的操作反过来,下文会继续提到),因此。这是因为旋转矩阵是一个正交矩阵。
求变换矩阵的方法
在变换中,无非就是将点,表示成矩阵形式就是
即
我们所做的就是求,那么既然一个变换矩阵会对这个物体的所有点都起作用,那么也一定对一些特殊点起作用,那么我们就可以利用几个简单的特殊点来进行问题的求解,例如上面旋转矩阵的例子中,有和
用矩阵表示就是
通过第一个矩阵等式我们直接就能求出和,再代入第二个矩阵求出和即可,最终我们就能求得旋转矩阵为
这也启发了我们,如果想求一个变换矩阵,只需要将变换前后的矩阵列出来,再代入特殊点求解即可。
Translation
平移操作我们可以很简单地将一个点操作前后的坐标写出来,即
但是我们会发现,我们不能将其表达成两个矩阵相乘的形式,这个操作的矩阵形式为
这样一来,这里的变换就不是线性变换了,它就只能是一种特殊的变换了。
齐次坐标
在发现这件事之后人们就开始思考,有没有一种方法能将它表达成线性变换?答案是有,这就引入了齐次坐标(Homogeneous Coordinates) 的概念。人们引入了一种新的形式来表示物体的坐标,他们在二维坐标后面又加了一个分量,规定
这样,平移的变换就可以写成线性变换形式,即
为什么在二维点的后面增加了而在二维向量的后面增加了呢?其实是有意义的。因为我们知道,在空间里,两向量和必为一个新的向量,如果坐标最后是,那么相加后最后还是;如果空间中的点,如果一个点减一个点,这样就形成了一个向量(末点-初点),我们发现最后的坐标是时相减得到,变成了一个向量,刚好也满足;一个点加一个向量表示为一个点沿着一个方向移动,移动到了一个新的点上,最后得到的还是一个点,最后的分量还是,也可验证。因此第三个分量的引入,在点上加一个,在向量上加一个,保证了这些操作最后的结果是对的。
那么最后一个,两点相加后,最后的分量是,这是什么意思呢?人们也扩充了它的定义,即齐次坐标表示的是二维点。因此两点相加表达的是它们的中点。
在引入齐次坐标之前,我们做平移、旋转等操作可以起个名字叫做仿射变换(Affine Transformation),仿射(Affine map) = linear map + translation,即
使用齐次坐标来表达就可以写成
下面利用齐次坐标来重新书写我们前面所学的各种变换
逆变换和组合变换
逆变换
逆变换的意义其实就是将矩阵变换的操作反过来,例如矩阵通过旋转矩阵得到,那么如果我们已知变换后的矩阵想求得只需要左乘旋转矩阵的逆,即
组合变换
复杂的变换可以通过多个变换组合来得到,其中,变换顺序至关重要,因为矩阵相乘不满足交换律。做一次变换可以理解为左乘一个矩阵,当然我们也可以将多次变换的矩阵的作用效果看作是一个矩阵的作用效果,因为矩阵相乘满足结合律。
那么如图所示,我们想求左侧矩阵变换到最右侧矩阵的效果,需要先旋转再平移,那么我们可以写成
对于一个起点不在原点的物体做宣传操作该如何做呢?我们可以先把物体移动到原点,做旋转操作后再移动回原来的位置,即
三维变换
了解了二维变换之后,三维就变得很简单了,首先我们依然引入齐次坐标,得到三维点和三维向量:
并且一般来说,齐次坐标表示的是三维点。
当然,我们也可以用的矩阵表达仿射变换,即
对应的三维中的各种变换表达如下:
注意,绕轴旋转时,左下角变为,右上角变为,这是因为轴的顺序问题造成的,我们说轴的顺序是,有,,但是根据右手定则,得到,而不是,因此这里是反的。
对于一般性旋转我们可以将其转化成三个轴的旋转,即
如果想绕着任意一个点旋转,我们可以先将物体沿着这个点平移至原点,做旋转操作后再移回该点。
如果想绕任意轴旋转,我们需要Rodrigues' Rotation Formula,给定角度和轴,我们有
观测变换 (Viewing Transformation)
现实生活中,拍照片我们需要如下步骤:
(1) 找一个好的场地,集合所有人 (模型变换 Model Transformation)
(2) 找一个好的角度,放置相机 (视图变换 View Transformation)
(3) 拍照 (投影变换 Projection Transformation)
1. 定义相机
- 相机的位置
- 拍摄方向 (look-at / gaze direction)
- 向上方向 (up direction) (垂直于拍摄方向)
2.视图变换(View Transformation)
在现实生活中,假如在摄影棚里拍照,相同的人,相同的相机,相同的相对摆放位置,不管在哪一个摄影棚,拍出来的效果是一样的。也就是说,如果相机和所有物体(包括前景背景)都一起移动时,拍出来的照片一定是一样的。更抽象地说,当我们移动物体和移动相机没有相对运动时,拍出来的照片是一样的。那么我们可以将相机永远放在原点这个固定的位置上,物体都可以移动,相机永远不动,并且相机的向上方向为方向,看向。
通过变换将相机放到标准位置上
首先,相机原本在位置,向看,并且向上方向为,现在要把它变成固定在原点,向方向看,并且up方向为。那么我们可以先将相机从移到原点,然后再把观原点察方向旋转到上,再把向上方向旋转到,写成矩阵表达为
其中,将相机从移到原点很容易写出,将向量的三个分量各减去他们本身即可,为
但是如何把观原点察方向旋转到上,再把向上方向旋转到呢?这件事并不容易做,但是反过来,将旋转到,将旋转到,将旋转到很容易实现,它和我们需要做的操作是一个互逆的操作,因此我们只需要求这一操作的矩阵的逆即可。
需要注意的是,为了保证相对结果不变,我们要将场景中的所有物体都做这样的变换。
3. 投影变换(Projection Transformation)
投影包含两种投影方式:正交投影 (Orthographic Projection) 和透视投影 (Perspective Projection)
正交投影 (Orthographic Projection)
正交投影很简单,不管物体的远近,我们只需将它“挤”到某个平面上即可。投影到平面的操作步骤如下:
- 先将相机放到标准位置上(原点,看向,向上为)
- 移除掉物体的坐标
- 平移、缩放将结果映射到 (约定俗称的方法,方便后续计算)
上述方法是一个简单的理解方式,但在图形学中,还有更方便的一种操作:
- 首先定义空间中的立方体,只需定义立方体的左右在轴上的值,下上在轴上的值和前后在轴上的值(由于右手坐标系,远的值小于近),一共个数
- 将这个方体映射到标准立方体,将立方体的中心移动到原点,在将模型缩放至
实现方法:首先通过平移,将立方体中心移动至原点,然后再缩放,以长宽高都缩放至2为例,则
注意:
由于我们定义相机看向,所以近>远,这也是为什么在OpenGL中使用左手系,但左手系意味着
也可以用变换坐标系的方式来理解,道理是一样的,但是不直观、不好理解。
透视投影 (Perspective Projection)
透视投影是应用最广泛的投影,满足近大远小的性质,带来的视觉效果是平行线不再平行,相交于一点。
回顾一下我们之前关于齐次坐标的定义:
- 都表示三维空间中的一个相同点,因为也属于任何一个数
- 举个例子:和都表示点
透视投影是从一个点(相机)开始,往外延伸出的一个四棱锥,我们定义近平面和远平面,称为Frustum,和正交投影的区别在于远平面相对更大,这也是透视投影和正交投影的主要区别。
因此我们只需要在正交投影之前增加一步,将远平面先挤压至与近平面相同的尺寸。也就是说透视投影的过程为两个步骤:先将远平面挤压至近平面的尺寸,再进行正交投影()。在这一过程中,我们规定:
- 近平面永远不变
- 远平面上的点的值不会变(因为是在平面内挤压,值不变)
- 远平面的中心点挤压前后不变
从侧面看Frustum会发现远近平面与相机有着相似三角形的关系,从坐标来看就有,因此写成矩阵形式就有
那么我们从
可知
为了求第三行,我们需要另外两个条件:任意在近平面上的点都保持不变,任意在远平面上的点的坐标保持不变。
(1)在近平面上,点坐标的值其实是,代入
有
第三行为,那么我们可以求出的第三行的前两项为,即
现在还剩下两个未知数和,接着我们再使用第二个条件:任意在远平面上的点的坐标保持不变。
(2)在远平面上,点坐标的值是,代入
根据近平面我们得到的结果,将其展开得
根据远平面我们得到的结果,将其展开得
于是将两个展开式联立
得
因此
透视投影的矩阵变换为
之前已经把如何将透视投影转化成正交投影说明白了,透视投影转化成正交投影需要保证近和远两个平面都是不变的,大小上远平面要变成和近平面一样大。在表示立方体上我们需要 左右前后远近() 个值来表示立方体,既然远和近在正交投影和透视投影中都是一样的,就不用管它了,我们可以将Frustum变成一个长方体,问题在于我们如何定义这个Frustum?
如图所示,我们从摄像机出发看向某一个区域,如果假设看到的就是这个近的平面,那么我们可以定义一个宽度和高度,就好像我们在看一个显示器一样,我们需要定义一个宽高比 (aspect ratio),如。我们还需要定义另外一个概念,称为field-of-view,即能看到的角度的范围。假如我们在看一个屏幕,我们可以分别从相机出发与屏幕顶边的中点和底边的中点连出两条红线,它们所形成的夹角就是垂直可视角度 (field-of-view Y, fovY)。因此,定义一个视锥需要定义一个宽高比和垂直可视角度。有些游戏里有水平可视角度,这个可以通过长宽比和垂直可视角度推出。
有了这两个概念,我们就可以将它们和之前定义的空间中的长方体转化成同一个概念。如图所示,从侧面来看这个视锥体可以看到一个三角形,如果我们取垂直可视角度的,可以发现,也就是说,如果我们知道这个近平面的距离,就能知道这个屏幕一半的高度是多少,屏幕的最高点对应的值就是,最低点对应的值就是,也就是说,如果定义一个空间中的长方体,,根据宽高比即可求出水平方向上两边中点的坐标。