📌
Foundations of 3D CG
  • 前言
  • I 基础入门
    • 第一章 简介
    • 第二章 线性
    • 第三章 仿射
    • 第四章 坐标系相关性(Respect)
    • 第五章 图形学中的坐标系
    • (WIP) 第六章 Hello World 3D
  • II 旋转与插值
    • 第七章 四元数(有点技术性)
    • (WIP) 第八章 球形:轨迹与弧
    • (WIP) 第九章 平滑插值
  • III 相机与光栅化
    • 第十章 投影
    • 第十一章 深度
    • 第十二章 从顶点到像素
    • 第十三章 变化变量
  • IV 像素及其相关内容
    • 第十四章 材质
    • 第十五章 纹理映射
    • 第十六章 采样
    • 第十七章 重建
    • 第十八章 重采样
  • V 高级话题
    • 第十九章 色彩
    • 第二十章 什么是光线追踪
    • 第二十一章 光(偏技术)
    • 第二十二章 几何建模:基础简介
    • 第二十三章 动画:甚至不能称之为简介
  • 附录
    • A Hello World 2D
    • B 仿射函数
Powered by GitBook
On this page
  • 10.1 针孔相机
  • 10.2 基础数学模型
  • 10.3 变体
  • 10.3.1 缩放
  • 10.3.2 移轴
  • 10.3.3 其他
  • 10.4 应用情境(Context)
  • 练习

Was this helpful?

  1. III 相机与光栅化

第十章 投影

Previous(WIP) 第九章 平滑插值Next第十一章 深度

Last updated 2 years ago

Was this helpful?

目前,我们已经在三维空间中描述了物体(objects)和视点(eye)。我们下一步的目标,是要理解如何将这些描述信息,转换为一张从视点处观察看到的二维图像。为此,我们需要建立一个简单的摄影机模型。通篇我们假设摄影机位于视点坐标系(e⃗t \vec{\mathbf{e}}^tet)的原点,并且看向视点的负z zz轴方向。我们使用[xe,ye,ze,1]t [x_e, y_e, z_e, 1]^t[xe​,ye​,ze​,1]t记号来表示视点坐标系下的一点。

10.1 针孔相机

最简单的摄影机形式是针孔相机(图10.1)。光线朝着摄影机的成像面运动,大部分会被一个位于ze=0 z_e = 0ze​=0的不透光平面所阻挡。但是,我们在该平面的中心设置一个非常小的孔洞,它位于视点坐标系的[0,0,0,1]t [0, 0, 0, 1]^t[0,0,0,1]t处。

在真实的物理世界中,任何真实的相机都需要具有一个有限大小的光圈,从而定量的可被测量的光能够从光圈中通过,并抵达摄影机成像面。并且,一旦光圈是有限大小的量,就需要一个镜头来辅助更好的将入射光“组织”并聚焦。不过,这些问题现在并不对我们造成困扰,因为我们不是要依据物理法则制造真实的相机。第 21 章有关于此话题更详细的内容。

在针孔相机中,摄影机成像面上的数据需要进行翻转,才能得到我们想要的照片。从数学角度看,通过将针孔相机与摄影机成像面置于针孔的前方,例如ze=−1 z_e = -1ze​=−1平面(图10.2),我们可以省略掉翻转图像这个步骤。在真实世界中,这是不现实的,但对于针孔相机的数学模型来说,这样做完全可行。

当创建好图像之后,如果我们将它固定在ze=−1 z_e = -1ze​=−1平面,并且在原点处观察(图10.3),对我们来说观察到的结果会与直接观察原始场景相同。我们精确再现了从原点处观察场景时,抵达我们眼部的数据。如果在空间中移动这幅图像,例如离眼睛近一点或远一点,我们就不再精确再现原始场景的视觉刺激信息了,但是它仍旧会看起来是一个视觉上可信的原始场景。

10.2 基础数学模型

针孔相机在数学上非常容易建模。我们使用坐标系[xn,yn]t [x_n, y_n]^t[xn​,yn​]t来表示摄影机成像面上的点。目前,我们在摄影机成像面上选定了一个二维坐标系统,它恰好与视点坐标系相匹配(也就是,xn=xex_n = x_exn​=xe​ 且 yn=yey_n=y_eyn​=ye​),但是我们很快会放宽这一设定。

给定空间中一点 ppp 在视点坐标系下的坐标 [xe,ye,ze,1]t[x_e, y_e, z_e, 1]^t[xe​,ye​,ze​,1]t,可以轻易看出(根据相似三角形定理)从点p pp 射向原点的射线,与成像面相交的位置为:

xn=−xeze(10.1) x_n = - \frac{x_e}{z_e} \tag{10.1}xn​=−ze​xe​​(10.1)
yn=−yeze(10.2) y_n = - \frac{y_e}{z_e} \tag{10.2}yn​=−ze​ye​​(10.2)

我们可以将其用矩阵运算表达为:

[10000100−−−−00−10][xeyeze1]=[xcyc−wc]=[xnwnynwn−wn],(10.3) \left[ \begin{matrix} 1 & 0 & 0 & 0 \\ 0 &1 & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \left[ \begin{matrix} x_e \\ y_e \\ z_e \\ 1 \end{matrix} \right] = \left[ \begin{matrix} x_c \\ y_c \\ - \\ w_c \end{matrix} \right] = \left[ \begin{matrix} x_nw_n \\ y_nw_n \\ - \\ w_n \end{matrix} \right] , \tag{10.3}​10−0​01−0​00−−1​00−0​​​xe​ye​ze​1​​=​xc​yc​−wc​​​=​xn​wn​yn​wn​−wn​​​,(10.3)

其中横线(—)意味着“我们不关心此处的值”。这个矩阵称作投影矩阵(projection matrix)。矩阵相乘的原始输出,[xc,yc,−,wc]t [x_c, y_c, -, w_c]^t[xc​,yc​,−,wc​]t,称作点 p~ \tilde{p}p~​ 的裁切坐标系向量(clip coordinates)。(如此命名,是因为这个原始数据随后会在裁切阶段使用,是第12.1节的内容)。wn=wc w_n = w_cwn​=wc​ 是一个新的变量,称作w ww分量(w ww-coordinate)。在这样的裁切坐标空间里,第四个分量不一定必须为 0 或 1。

我们说 xnwn=xc x_nw_n = x_cxn​wn​=xc​,并且 ynwn=yc y_nw_n = y_cyn​wn​=yc​。如果想要单独提取 xn x_nxn​,必须进行除法 xn=xnwnwn x_n = \frac{x_nw_n}{w_n}xn​=wn​xn​wn​​,(对 yn y_nyn​ 也是这样)。当这么做的时候,我们恢复了等式 10.1 中的计算,即简单摄影机模型使用的计算。(译者注:因为此时 −ze=wc=wn -z_e = w_c = w_n−ze​=wc​=wn​)

我们得到的附有脚标 nnn 的输出坐标系向量,称作归一化设备坐标系(normalized device coordinates),因为它用与具体像素数无关的抽象单位,表达了图像上的点。在计算机图形学中,我们将所有的图像数据都维持在正则正方形内(canonical square) −1≤xn≤+1,−1≤yn≤+1 -1 \leq x_n \leq +1,-1 \leq y_n \leq +1−1≤xn​≤+1,−1≤yn​≤+1,并最终将其映射到屏幕上的一个窗口中。这个正方形之外的数据,不会被记录或显示。这正是我们在附录 A 中用来描述 2D OpenGL 绘制的模型。

10.3 变体

通过改变投影矩阵中的项,我们可以对摄影机的几何变换进行微调。

10.3.1 缩放

如果我们将摄影机成像面移动到 ze=n z_e= nze​=n 处,其中n nn 是一个负数(图10.4),我们可以将该摄影机建模为

xn=xenzex_n = \frac{x_en}{z_e}xn​=ze​xe​n​
yn=yenzey_n = \frac{y_en}{z_e}yn​=ze​ye​n​

这样做类似于镜头上的变焦。用矩阵来表达则是

[xnwnynwn−wn]=[−n0000−n00−−−−00−10][xeyeze1](10.3)\left[ \begin{matrix} x_nw_n \\ y_nw_n \\ - \\ w_n \end{matrix} \right] = \left[ \begin{matrix} -n & 0 & 0 & 0 \\ 0 & -n & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \left[ \begin{matrix} x_e \\ y_e \\ z_e \\ 1 \end{matrix} \right] \tag{10.3}​xn​wn​yn​wn​−wn​​​=​−n0−0​0−n−0​00−−1​00−0​​​xe​ye​ze​1​​(10.3)

这样实际上相当于,在原先成像面位于 ze=−1 z_e = -1ze​=−1 的相机的基础上,将记录到的图像以 −n -n−n 为系数进行缩放,并最终只保留图像位于正则正方形内的部分(如图10.5)。因此,后面我们最好不再将归一化设备坐标系向量(normalized device coordinates),看作位于成像面上、与某一视点坐标系相吻合的坐标系,而是简单的认为它是图像平面上固有的一个坐标系。

控制缩放系数的一个好方法是,通过给定目标摄影机的垂直视场角度(field of view)来决定 −n -n−n 。尤其是当我们想要让摄影机的视场角为 θ \thetaθ 度的时候。这时我们就可以设定 −n=1tan⁡(θ2) -n = \frac{1}{\tan(\frac{\theta}{2})}−n=tan(2θ​)1​(如图10.6),由此我们得到投影矩阵为

[1tan⁡(θ2)00001tan⁡(θ2)00−−−−00−10](10.4)\left[ \begin{matrix} \frac{1}{\tan(\frac{\theta}{2})} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan(\frac{\theta}{2})} & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \tag{10.4}​tan(2θ​)1​0−0​0tan(2θ​)1​−0​00−−1​00−0​​(10.4)

我们可以直接验证,任意以原点为起点,并与负 z zz 轴在垂直方向形成的夹角为 θ2 \frac{\theta}{2}2θ​ 度的射线,都会被映射到成像面上正则正方形的边界上,因此,该相机的视场角是 θ \thetaθ 度。 例如,视点坐标系向量 [0,tan⁡(θ2),−1,1]t [0, \tan(\frac{\theta}{2}), -1, 1]^t[0,tan(2θ​),−1,1]t 映射到归一化设备坐标系为 [0,1]t [0, 1]^t[0,1]t。

更通用的处理问题,我们可以用不同的水平和垂直缩放系数 sx s_xsx​ 和 sy s_ysy​ 来分别缩放原始图像,从而得到如下的摄影机模型:

\begin{eqnarray} \left[ \begin{matrix} x_nw_n \\ y_nw_n \\ - \\ w_n \end{matrix} \right] &=& \left[ \begin{matrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 1 & 0 \end{matrix} \right] \left[ \begin{matrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \left[ \begin{matrix} x_e \\ y_e \\ z_e \\ 1 \end{matrix} \right] \\ &=& \left[ \begin{matrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \left[ \begin{matrix} x_e \\ y_e \\ z_e \\ 1 \end{matrix} \right] \end{eqnarray}

在计算机图形学中,这种不等比的缩放在处理不为正方形的屏幕窗口时会用到。假设窗口的宽大于高。在摄影机变换中,我们需要在水平方向上挤压物体,以便更宽的水平视场角可以被容纳进我们留存的正则正方形内。当数据随后映射到窗口中时,它会被相应的拉伸从而看起来没有变形。

定义 a a a ,即屏幕的宽高比(aspect ratio)为它的宽度除以高度(例如以像素为单位)。我们可以将投影矩阵设置为

[1atan⁡(θ2)00001tan⁡(θ2)00−−−−00−10](10.5)\left[ \begin{matrix} \frac{1}{a\tan(\frac{\theta}{2})} & 0 & 0 & 0 \\ 0 & \frac{1}{\tan(\frac{\theta}{2})} & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \tag{10.5}​atan(2θ​)1​0−0​0tan(2θ​)1​−0​00−−1​00−0​​(10.5)

这个相机的垂直方向与等式(10.4)中保持不变,但是在生成图像时,采用了相应更宽的水平视场角。

当窗口的高大于宽,也就是 a<1 a < 1a<1 时,我们依然可以使用等式 10.5 中的矩阵,但我们也许并不满意这样做得到的水平视场角,因为它很狭窄。如果我们希望 θ \thetaθ 为垂直或是水平视场角中最小的那个,那么,当 a<1 a < 1a<1 时,我们需要使用如下的投影矩阵:

[1tan⁡(θ2)0000atan⁡(θ2)00−−−−00−10](10.5)\left[ \begin{matrix} \frac{1}{\tan(\frac{\theta}{2})} & 0 & 0 & 0 \\ 0 & \frac{a}{\tan(\frac{\theta}{2})} & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \tag{10.5}​tan(2θ​)1​0−0​0tan(2θ​)a​−0​00−−1​00−0​​(10.5)

这正是在 6.2 小节中我们调用 makeProjection 所产生的矩阵。

在计算机图形学中,选用视场角时常常需要去平衡不同的因素。一方面,更广的视场角,例如在一个游戏环境中,可以让观察者看到四周更多的东西。另一方面,在一个典型的观察环境下(除非有人把脸直接贴在屏幕前面),屏幕只占据了观察者四周环境的很小一部分角度。在这种情况下,观察者看到的几何关系会与图像环境中的不一致(就像图 10.3 中那样),并且图像看起来会有点变形(例如球体可能显示时不是圆形)。

10.3.2 移轴

不太常见的是,也许我们会希望用 [cx,cy]t [c_x, c_y]^t[cx​,cy​]t 来移动 2D 归一化设备坐标系。这可以用投影矩阵建模为

\begin{eqnarray} \left[ \begin{matrix} x_nw_n \\ y_nw_n \\ - \\ w_n \end{matrix} \right] &=& \left[ \begin{matrix} 1 & 0 & 0 & c_x \\ 0 & 1 & 0 & c_y \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{matrix} \right] \left[ \begin{matrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \left[ \begin{matrix} x_e \\ y_e \\ z_e \\ 1 \end{matrix} \right] \\ &=& \left[ \begin{matrix} 1 & 0 & -c_x & 0 \\ 0 & 1 & -c_y & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \left[ \begin{matrix} x_e \\ y_e \\ z_e \\ 1 \end{matrix} \right] \end{eqnarray}

对应到摄影机上,这个矩阵就描述了一个移轴的成像面(shifted film plane)(如图10.7)。它也许看起来很罕见,但实际上,因为制造工艺以及光学的问题,大部分真实的相机内部都确实有一些微量的偏移。

在计算机图形学中,移轴相机的主要用途是平铺显示(如图10.8),我们将多个显示屏并列放置来组成一个更大的屏幕。在这种情况下,这些每一个子图像都以一个适当的移轴相机来建立数学模型。移轴的另一个应用是创建一对图像,用于单屏幕上的立体显示。

通常在计算机图形学中,表示移轴(以及缩放)的相机时,会首先给定一个近切平面 ze=n z_e = nze​=n。通过指定一个视点坐标系下,与坐标轴平行的长方形,可以在这个平面上确定一个长方形。(为了得到没有变形的输出结果,这个长方形的宽高比应该与最终的窗口相吻合。)用 l,r l, rl,r 的值来表示这个长方形在 xe x_exe​ 坐标轴上的左右边界,并且用 t,b t, bt,b 来表示它在 ye y_eye​ 坐标轴上顶和底部的边界。总的来说,这些参数描述了空间中一个 3D 视锥(frustum)的形状。穿过这个长方形并射入原点的射线,会被映射到正则正方形图像内,使用如下投影矩阵描述

[−2nr−l0r+lr−l00−2nt−bt+bt−b0−−−−00−10](10.6)\left[ \begin{matrix} -\frac{2n}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & -\frac{2n}{t-b} & \frac{t+b}{t-b} & 0 \\ - & - & - & - \\ 0 & 0 & -1 & 0 \end{matrix} \right] \tag{10.6}​−r−l2n​0−0​0−t−b2n​−0​r−lr+l​t−bt+b​−−1​00−0​​(10.6)

(如图 10.9)。

10.3.3 其他

在矩阵式(10.6)左上角 2 乘 2 的区域中,留有两个零在我们的摄影机模型中没有涉及到,这是一个典型的情况。这块 2 乘 2 的区域作为一个整体,可以表示像素网格的旋转与切变。切变在真实摄影机中并不常见,在计算机图形学中也不常用。当然,我们可以围绕着摄影机的光轴来旋转整个摄影机,但是这种旋转,在最初定义视点坐标系 e⃗t\vec{\mathbf{e}}^tet 时,就可以用适当的摄影机旋转来表示!

10.4 应用情境(Context)

我们所描述的投影操作是一种映射,它可以在视点坐标系下应用在任何给定的点上,从而得到该点的归一化设备坐标系。尽管在 OpenGL 中,这一映射只被应用于三角形的顶点上。一旦得到了三角形上三个顶点的归一化设备坐标系向量,通过计算图像平面上位于三角形内部的所有像素,就可以简单的得到三角形内部的点。

练习

10.1 假设我们给一个冰做的立方体拍照。将它正面的投影绘制出来,用实线表示靠前的面,用虚线表示背部的面。假设用 40,30 和 20 度的视场角来分别拍摄三张图像。摄影机的其他参数保持不变。那么以下的图像序列哪一个比较可信:

10.2 (发散思考)看以下两张照片: 你能否看出这些照片是如何拍摄的?两张照片使用的视点坐标系和投影矩阵有何不同?(提示:记住我们可以制造出移轴相机。)

(版权信息:图片来自[26],版权属于 Phillip Greenspun)

10.3 (发散思考)设 e⃗t=w⃗tE \vec{\mathbf{e}}^t = \vec{\mathbf{w}}^tEet=wtE ,且 P PP 是一个摄影机矩阵,是一个任意的 4 乘 4 矩阵,且第三行以"—"填充。给定 6 个点的世界坐标以及它们的归一化设备坐标,如何计算矩阵 PE−1 PE^{-1}PE−1 中的 12 个未知项?(注意:必须包含未知的缩放系数才能解出这个问题)(提示:用 0 建立一个适当的右手性齐次线性系统)

一个 2D 针孔相机。从场景中几何体表面射出的光线(图中虚线表示)穿过小孔,并将其颜色记录在位于 ze = 1 处的成像面。其中部分经此着色的像素会被显示出来。图中蓝色的六面体被遮挡住,因此没有出现在画面中。
基本的针孔相机可以建模为成像面位于 ze = -1 处,即位于原点前方。图像平面上最上方的点 yn 在归一化设备坐标系中的值为 1。而最下方的 yn 则为 -1。位于同一虚线上的点,会映射到图像的同一像素上。
如果成像面被最终得到的画面替代,而我们的视点位于视角坐标系原点,那么抵达的数据,与原始的场景带来的数据将会没有区别。
在这个摄影机模型中,我们将成像面移动至 ze=nz_e = nze​=n 平面。我们仍然在成像面内保持比例 -1 < yn = ye < 1。这一做法相当于对长焦镜头的模拟。
经过变焦的摄影机,等同于一个成像面位于 ze = -1,但图像坐标系经过缩放 yn = - n*ye 的摄影机。
摄影机的缩放可以方便的使用垂直视场角:θ 来表示。此时,我们可以认为位于 ze = -1 的成像面的边界为 -tan(θ/2) < ye < tan(θ/2),映射到归一化设备坐标系的 -1 < yn < 1。
在移轴摄影机上,我们平移归一化设备坐标系,将 [-1 ... 1] 的坐标区域保持在移动后的坐标系内。
在这样的设定中,观察者看到的是由两个显示平面共同创建的图像。每一个单独的画面应建模为移轴相机。
通过指定近切平面的矩形图像,可以定义 3D 视锥。