7.1 观察变换(Viewing Transformations)
观察变换是指将 3D 的物体映射到 2D 的过程。这是一个复杂的过程,我们将他拆解成几个过程:
- 摄像机变换(camera transformation)或者 眼睛变换(eye transformation):在场景内的物体摆放好之后,将摄像机拜访到场景的原点,这是一个刚体变换,只跟旋转和平移有关,不会产生形变或者缩放。过程完成后为 相机空间(camera space)。
- 投影变换(projection transformation):将相机视野映射到 的立方体中。叫做 规则观察体(canonical view volume) 或者 标准视景体(我就叫它 标准视图体积 了),也有的将过程完成后的空间叫做 裁剪空间(clip space) 或者 标准化设备坐标(normalized device coordinates)。
-
视口变换(viewport transformation) 或者 窗口变换(windowing transformation):将上面的立方体映射到屏幕像素空间。过程结束之后叫做 屏幕空间(screen space) 或者 像素坐标(pixel coordinates)。
(注意:有的 API 的 Viewing Transformations 只是单纯指这里的 camera transformation)
先从视口变换开始讲起,比如现在物体都已经在裁剪空间了,如下图(右手系),我们需要将 映射到屏幕最左边,将 映射到屏幕最右侧,将 映射到屏幕的最底部,将 映射到屏幕的最顶部。也就是从上往下看,顺着 的方向。
我们约定屏幕空间坐标:
所以假设我们的屏幕有 个像素,那么它的范围有应该是 ,我们需要把 映射到这个范围中。先将 扩大到 ,然后再平移到屏幕的中心即可,公式为(注意,这里我们忽略了 轴,这并不影响现在的计算,关于 轴,我们后面再说):
正交视图体积(orthographic view volume)跟标准标准视图体积类似,但它不是 ,而是 ,如图:
字母代表的意思和所在轴如下所示:
我们观察的时候,其实是顺着 看的,所以 离我们更近, 离我们更远,并且 ,观察角度示意图如下:
将正交视图体积变成标准视图体积的矩阵如下:
推导过程如图(上面的 也是类似的过程):
所以要将正交视图体积中的点映射屏幕上,整个过程就是:
汇编代码为:
介绍完了视口变化,正交投影变换(其实还有透视投影变换,后面会说),再前面就是摄像机变换。下图中 是一组正交基,我们的眼睛(摄像机)在 的位置,看向 的方向, 是我们头顶的方向。
通过 来计算出 ,用两次叉乘即可(注意都是右手坐标系):
如下图所示,我们的目标就是把物体都转换到 坐标系中,但不幸的是(?),物体目前都是世界坐标,也就是在 坐标。
现在我们需要把 坐标转换到 坐标。公式如下(上一章学过了):
在这里写成:
我写一下完整过程:
总之 可以直接用。(算了一圈才发现从几何意义来理解,平移 ,再平移 不就回来了... )
我太蠢了
代码跟上面差不多,多乘了一个。
7.2 投影变换(Projective Transformations)
如图所示,相机的观测点还是在 点,目光像 看去, 是 应该在屏幕上的长度,用相似三角形可以得到 ,这里有一个乘法,所以我们不能够像上面一样再多乘一个矩阵来保持算法不变,就完成投影变换。
回顾一下,没有其次坐标之前,我们可以完成这样的计算:
再引进其次坐标之后,多了平移,可以加上一个常数:
现在我们可以再进一步,把齐次坐标的最下面一行充分利用起来,让它不再一直是 ,还有点的 也不必一直都是 。这样就可以实现带除法的运算:
但是这样有一个限制,也就是对于每个分量,他们的分母都是相同的:
实现这种变换的矩阵写成这样:
当我们需要在笛卡尔坐标系中运用到变换后的点的时候,将 归一,也就是同时除 即可:
这个过程就叫做 投影变换(projective transformation) 或者 单应性矩阵(homography)。
举个例子,这个矩阵
把下图的标准矩阵进行了透视投影。
对于 这个点,经过变化后为:
再将其 归一化得到 这个点。还有更聪明的方法来解决这个问题,我们规定所有向量的标量倍乘都代表同一个点:
~ 代表 相等 的意思,也就是说 和 这两个其次坐标在空间中代表同一个点。举个例子,下面是一个一维的齐次坐标,这一条线上的点我们在一维上都认为是同一个点,也就是 这个点。
下面是一个二维的齐次坐标,这条直线上的所有点我们都认为是 点,其实这并没有问题,因为他们的不同是在三维的空间,但我们探究的是在二维上的相等性,只要我们的规则能让其在塌缩到二维的时候是一样的就 Ok。
这样我们就可以进行任意次变换,而不用担心 的值,甚至在某一瞬间 也是可以的,我们只需要在真正需要读出这个点的值的时候才将每个分量除 就可以了。
7.3 透视投影(Perspective Projection)
还是看这个图,里面的 换成 ,并且想象一下在屏幕的例外方向存在 ,根据相似三角形,我们知道,。所以写出下面的矩阵,它的第一行,第二行,第四行在计算中就保证了刚刚的式子,第三行关于 的,我们跟之前的正交投影和视口变换一样,先不管。
因为对于齐次坐标来说,同时乘常量没有关系,所以 ~ 两边的坐标我们看成是相等的,能够看出是满足 , 的。
上述矩阵对视椎体的作用如下面两个图所示,保证了在 这个平面上的点是不动的,并且在 这个平面上的点是被挤压到里面,中间的保持原本的顺序。
的逆矩阵是:
当然我们也可以让它的形式变得好看一点:
当把透视投影变成正交投影的样子之后就可以用正交投影的矩阵来完成后面的操作了:
其中 :
透视投影的整个观察变换代码如下:
除了多乘了一个矩阵之外,还要除 。
在 OpenGL 中,透视投影矩阵写法跟我们差不多:
还有一些地方,会把 ,变成 的形式。区别都不是很大。
7.5 视场角(FOV)
有时候我们会觉得 的表示方法太麻烦,第一步化简:
如果像素是正方形的,并且分辨率是定的,那么 和 的比率其实也是定的:
最后有一个视场角的概念,可以看图,公式如下:
所以只需要屏幕分辨率、视场角、,我们就可以求出透视投影矩阵。
网友评论