目前,我们已经在三维空间中描述了物体(objects)和视点(eye)。我们下一步的目标,是要理解如何将这些描述信息,转换为一张从视点处观察看到的二维图像。为此,我们需要建立一个简单的摄影机模型。通篇我们假设摄影机位于视点坐标系(et)的原点,并且看向视点的负z轴方向。我们使用[xe,ye,ze,1]t记号来表示视点坐标系下的一点。
10.1 针孔相机
最简单的摄影机形式是针孔相机(图10.1)。光线朝着摄影机的成像面运动,大部分会被一个位于ze=0的不透光平面所阻挡。但是,我们在该平面的中心设置一个非常小的孔洞,它位于视点坐标系的[0,0,0,1]t处。
在真实的物理世界中,任何真实的相机都需要具有一个有限大小的光圈,从而定量的可被测量的光能够从光圈中通过,并抵达摄影机成像面。并且,一旦光圈是有限大小的量,就需要一个镜头来辅助更好的将入射光“组织”并聚焦。不过,这些问题现在并不对我们造成困扰,因为我们不是要依据物理法则制造真实的相机。第 21 章有关于此话题更详细的内容。
在针孔相机中,摄影机成像面上的数据需要进行翻转,才能得到我们想要的照片。从数学角度看,通过将针孔相机与摄影机成像面置于针孔的前方,例如ze=−1平面(图10.2),我们可以省略掉翻转图像这个步骤。在真实世界中,这是不现实的,但对于针孔相机的数学模型来说,这样做完全可行。
当创建好图像之后,如果我们将它固定在ze=−1平面,并且在原点处观察(图10.3),对我们来说观察到的结果会与直接观察原始场景相同。我们精确再现了从原点处观察场景时,抵达我们眼部的数据。如果在空间中移动这幅图像,例如离眼睛近一点或远一点,我们就不再精确再现原始场景的视觉刺激信息了,但是它仍旧会看起来是一个视觉上可信的原始场景。
10.2 基础数学模型
针孔相机在数学上非常容易建模。我们使用坐标系[xn,yn]t来表示摄影机成像面上的点。目前,我们在摄影机成像面上选定了一个二维坐标系统,它恰好与视点坐标系相匹配(也就是,xn=xe 且 yn=ye),但是我们很快会放宽这一设定。
给定空间中一点 p 在视点坐标系下的坐标 [xe,ye,ze,1]t,可以轻易看出(根据相似三角形定理)从点p 射向原点的射线,与成像面相交的位置为:
xn=−zexe(10.1) yn=−zeye(10.2) 我们可以将其用矩阵运算表达为:
10−001−000−−100−0xeyeze1=xcyc−wc=xnwnynwn−wn,(10.3) 其中横线(—)意味着“我们不关心此处的值”。这个矩阵称作投影矩阵(projection matrix)。矩阵相乘的原始输出,[xc,yc,−,wc]t,称作点 p~ 的裁切坐标系向量(clip coordinates)。(如此命名,是因为这个原始数据随后会在裁切阶段使用,是第12.1节的内容)。wn=wc 是一个新的变量,称作w分量(w-coordinate)。在这样的裁切坐标空间里,第四个分量不一定必须为 0 或 1。
我们说 xnwn=xc,并且 ynwn=yc。如果想要单独提取 xn,必须进行除法 xn=wnxnwn,(对 yn 也是这样)。当这么做的时候,我们恢复了等式 10.1 中的计算,即简单摄影机模型使用的计算。(译者注:因为此时 −ze=wc=wn)
我们得到的附有脚标 n 的输出坐标系向量,称作归一化设备坐标系(normalized device coordinates),因为它用与具体像素数无关的抽象单位,表达了图像上的点。在计算机图形学中,我们将所有的图像数据都维持在正则正方形内(canonical square) −1≤xn≤+1,−1≤yn≤+1,并最终将其映射到屏幕上的一个窗口中。这个正方形之外的数据,不会被记录或显示。这正是我们在附录 A 中用来描述 2D OpenGL 绘制的模型。
10.3 变体
通过改变投影矩阵中的项,我们可以对摄影机的几何变换进行微调。
10.3.1 缩放
如果我们将摄影机成像面移动到 ze=n 处,其中n 是一个负数(图10.4),我们可以将该摄影机建模为
xn=zexen yn=zeyen 这样做类似于镜头上的变焦。用矩阵来表达则是
xnwnynwn−wn=−n0−00−n−000−−100−0xeyeze1(10.3) 这样实际上相当于,在原先成像面位于 ze=−1 的相机的基础上,将记录到的图像以 −n 为系数进行缩放,并最终只保留图像位于正则正方形内的部分(如图10.5)。因此,后面我们最好不再将归一化设备坐标系向量(normalized device coordinates),看作位于成像面上、与某一视点坐标系相吻合的坐标系,而是简单的认为它是图像平面上固有的一个坐标系。
控制缩放系数的一个好方法是,通过给定目标摄影机的垂直视场角度(field of view)来决定 −n 。尤其是当我们想要让摄影机的视场角为 θ 度的时候。这时我们就可以设定 −n=tan(2θ)1(如图10.6),由此我们得到投影矩阵为
tan(2θ)10−00tan(2θ)1−000−−100−0(10.4) 我们可以直接验证,任意以原点为起点,并与负 z 轴在垂直方向形成的夹角为 2θ 度的射线,都会被映射到成像面上正则正方形的边界上,因此,该相机的视场角是 θ 度。 例如,视点坐标系向量 [0,tan(2θ),−1,1]t 映射到归一化设备坐标系为 [0,1]t。
更通用的处理问题,我们可以用不同的水平和垂直缩放系数 sx 和 sy 来分别缩放原始图像,从而得到如下的摄影机模型:
\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 ,即屏幕的宽高比(aspect ratio)为它的宽度除以高度(例如以像素为单位)。我们可以将投影矩阵设置为
atan(2θ)10−00tan(2θ)1−000−−100−0(10.5) 这个相机的垂直方向与等式(10.4)中保持不变,但是在生成图像时,采用了相应更宽的水平视场角。
当窗口的高大于宽,也就是 a<1 时,我们依然可以使用等式 10.5 中的矩阵,但我们也许并不满意这样做得到的水平视场角,因为它很狭窄。如果我们希望 θ 为垂直或是水平视场角中最小的那个,那么,当 a<1 时,我们需要使用如下的投影矩阵:
tan(2θ)10−00tan(2θ)a−000−−100−0(10.5) 这正是在 6.2 小节中我们调用 makeProjection
所产生的矩阵。
在计算机图形学中,选用视场角时常常需要去平衡不同的因素。一方面,更广的视场角,例如在一个游戏环境中,可以让观察者看到四周更多的东西。另一方面,在一个典型的观察环境下(除非有人把脸直接贴在屏幕前面),屏幕只占据了观察者四周环境的很小一部分角度。在这种情况下,观察者看到的几何关系会与图像环境中的不一致(就像图 10.3 中那样),并且图像看起来会有点变形(例如球体可能显示时不是圆形)。
10.3.2 移轴
不太常见的是,也许我们会希望用 [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。通过指定一个视点坐标系下,与坐标轴平行的长方形,可以在这个平面上确定一个长方形。(为了得到没有变形的输出结果,这个长方形的宽高比应该与最终的窗口相吻合。)用 l,r 的值来表示这个长方形在 xe 坐标轴上的左右边界,并且用 t,b 来表示它在 ye 坐标轴上顶和底部的边界。总的来说,这些参数描述了空间中一个 3D 视锥(frustum)的形状。穿过这个长方形并射入原点的射线,会被映射到正则正方形图像内,使用如下投影矩阵描述
−r−l2n0−00−t−b2n−0r−lr+lt−bt+b−−100−0(10.6) (如图 10.9)。
10.3.3 其他
在矩阵式(10.6)左上角 2 乘 2 的区域中,留有两个零在我们的摄影机模型中没有涉及到,这是一个典型的情况。这块 2 乘 2 的区域作为一个整体,可以表示像素网格的旋转与切变。切变在真实摄影机中并不常见,在计算机图形学中也不常用。当然,我们可以围绕着摄影机的光轴来旋转整个摄影机,但是这种旋转,在最初定义视点坐标系 et 时,就可以用适当的摄影机旋转来表示!
10.4 应用情境(Context)
我们所描述的投影操作是一种映射,它可以在视点坐标系下应用在任何给定的点上,从而得到该点的归一化设备坐标系。尽管在 OpenGL 中,这一映射只被应用于三角形的顶点上。一旦得到了三角形上三个顶点的归一化设备坐标系向量,通过计算图像平面上位于三角形内部的所有像素,就可以简单的得到三角形内部的点。
练习
10.1 假设我们给一个冰做的立方体拍照。将它正面的投影绘制出来,用实线表示靠前的面,用虚线表示背部的面。假设用 40,30 和 20 度的视场角来分别拍摄三张图像。摄影机的其他参数保持不变。那么以下的图像序列哪一个比较可信:
10.2 (发散思考)看以下两张照片: 你能否看出这些照片是如何拍摄的?两张照片使用的视点坐标系和投影矩阵有何不同?(提示:记住我们可以制造出移轴相机。)
(版权信息:图片来自[26],版权属于 Phillip Greenspun)
10.3 (发散思考)设 et=wtE ,且 P 是一个摄影机矩阵,是一个任意的 4 乘 4 矩阵,且第三行以"—"填充。给定 6 个点的世界坐标以及它们的归一化设备坐标,如何计算矩阵 PE−1 中的 12 个未知项?(注意:必须包含未知的缩放系数才能解出这个问题)(提示:用 0 建立一个适当的右手性齐次线性系统)