书籍翻译 – Fundamentals of Computer Graphics, Fourth Edition,第7章 Viewing中文翻译
7 视图变换
在上一章中,我们了解了如何使用矩阵变换在二维或三维空间中排列几何对象。几何变换的第二个重要用途是在三维位置和三维世界的二维视图中的位置之间移动对象。这种从3D到2D的映射称为视图变换,它在按顺序渲染对象的过程中起着重要作用,在此过程中,我们需要快速找到场景中每个对象的图像空间位置。
当我们在第4章学习光线跟踪时,我们介绍了不同类型的透视图和正交视图,以及如何根据任何给定视图生成可视光线。本章是关于这一过程的反面。这里我们解释如何使用矩阵变换来表示任何平行或透视视图。本章中的变换将场景(世界空间)中的3D点投影到图像(图像空间)中的2D点,并将给定像素的观察光线上的任何点投影回该像素在图像空间中的位置。
在阅读本章之前,建议先回顾第4章中有关透视图和光线生成的讨论。
就其本身而言,将点从世界空间投影到图像空间的能力仅适用于生成线框渲染(仅绘制对象的边缘),并且较近的曲面不会遮挡较远的曲面(图7.1)。正如光线跟踪器需要沿每条视线查找最近的曲面交点一样,显示实体外观对象的对象顺序渲染器必须计算在屏幕上任何给定点绘制的曲面中哪一个(可能有许多)最近,并仅显示该曲面。在本章中,我们假设我们绘制的模型仅包含两个端点的(x,y,z)坐标指定的三维线段。后面的章节将讨论生成实体表面渲染所需的方法。
7.1 视图变换
视图变换主要是将以(x,y,z)表示的3D坐标转化为在图像中以像素为单位的图像坐标。它是一个复杂的理论,取决于许多不同的东西,包括相机的位置和方向、投影类型、视野和图像的分辨率。与所有复杂的转换一样,最好将其分解为几个简单转换的产物。大多数图形系统通过使用三种转换序列来实现:
- 摄影机变换或眼睛变换,这是一种刚体变换,将摄影机以合适的方向放置在原点。它仅取决于摄影机的位置和方向或姿势。
- 投影变换,从摄影机空间投影点,使所有可见点的x和与都位于-1到1的范围内。这仅取决于所需的投影类型。
- 视口变换或窗口变换,将此单位图像矩形映射到像素坐标中所需的矩形。它只取决于输出图像的大小和位置
为了便于描述过程的各个阶段(图7.2),我们为这些输入和输出变换的变换命名为不同的坐标系。
摄影机变换将标准坐标(或世界空间)中的点转换为摄影机坐标或将其放置在摄影机空间中。
投影变换将点从摄影机空间移动到标准视图。
最后,视口转换将标准视图映射到屏幕空间。
每个转换都非常简单。我们将从视口变换开始详细讨论正交情况,然后讨论透视投影下的变换。
7.1.1 视口变换
我们从一个问题开始,该问题的解决方案将在任何可视条件下重复使用。我们假设要查看的几何体位于标准视图体积中, 我们希望用一个正射相机在−z方向观察它。标准视图体积是指 (x,y,z)\in \left [ -1,1 \right ] ^{3} 包含x,y,z坐标在-1和1之间的坐标范围。x=-1 处于屏幕的最左边,x = +1 处于屏幕的最右边,y = -1处于屏幕的底部,y = +1处于屏幕的顶部。
回想第3章中像素坐标的约定:每个像素“拥有”一个以整数坐标为中心的单位正方形;图像边界与像素中心之间有半个单位差距;最小像素中心坐标为(0,0)。如果我们正在绘制一幅像素为n_{x}\times n_{y} 的图片(或者屏幕上的窗口),我们需要将正方形\left [ -1,1 \right ] ^{2} 映射为矩形\left [ -0.5,n_{x}-0.5 \right ] \times \left [ -0.5,n_{y}-0.5 \right ] 。
现在,我们假设要绘制的所有线段都完全位于规范视图体积内。稍后我们将在讨论剪裁时放松该假设。
由于视口变换将一个轴对齐的矩形映射到另一个,因此这是由公式(6.6)给出的窗口变换的一种情况:
x_{\text {screen }} \\
y_{\text {screen }} \\
1
\end{array}\right]=\left[\begin{array}{ccc}
\frac{n_{x}}{2} & 0 & \frac{n_{x}-1}{2} \\
0 & \frac{n_{y}}{2} & \frac{n_{y}-1}{2} \\
0 & 0 & 1
\end{array}\right]\left[\begin{array}{c}
x_{\text {canonical }} \\
y_{\text {canonical }} \\
1
\end{array}\right]
请注意,此矩阵将忽略规范视图体积中点的z坐标,因为点沿投影方向的距离不会影响该点在图像中的投影位置。但在正式称之为“视口矩阵”之前,我们添加了一行和一列,以达到在不改变z坐标的情况下沿z坐标移动的目的。在本章中我们不需要它,但最终我们需要z值,因为它们可以用来使较近的曲面隐藏较远的曲面(参见第8.2.3节)。
\frac{n_{x}}{2} & 0 & 0 & \frac{n_{x}-1}{2} \\
0 & \frac{n_{y}}{2} & 0 & \frac{n_{y}-1}{2} \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{array}\right]
7.1.2 正交投影矩阵
我们通常希望在不是标准视图体积的一些空间区域中渲染几何体。第一步是保持观察的方向沿着-z方向,并保持y轴向上。我们不会替换视口矩阵,而是通过将其与右侧的另一个矩阵相乘来计算。
在这些约束条件下,视图范围是一个轴对齐的长方体\left [ l,r \right ] \times \left [ b,t \right ] \times \left [ f,n \right ] ,如图7.4所示。我们将此框称为正交视图体积,并参照边界平面,如下所示:
x=l \equiv \text { left plane } \\
x=r \equiv \text { right plane } \\
y=b \equiv \text { bottom plane } \\
y=t \equiv \text { top plane } \\
z=n \equiv \text { near plane } \\
z=f \equiv \text { far plane. }
\end{array}
假设一个观众沿着负z轴看,他的头指向y方向。这意味着n>f,这可能是不直观的,但如果您假设整个正交视图体积具有负z值,则z=n近平面更靠近观众当且仅当n>f时;这里f是比n小的数字,即绝对值大于n的负数。
图7.5显示这一概念。从正交视图体积到标准视图体积的转换是另一个窗口转换,因此我们可以简单地将正交视图体积和标准视图体积的边界替换为方程式(6.7),以获得此转换的矩阵:
\frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\
0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\
0 & 0 & \frac{2}{n-f} & -\frac{n+f}{n-f} \\
0 & 0 & 0 & 1
\end{array}\right]
要在正交视图体积中绘制三维线段,我们将其投影到屏幕x坐标和y坐标中,并忽略z坐标。我们通过组合方程(7.2)和(7.3)来实现这一点。请注意,在程序中,我们将矩阵相乘,形成一个矩阵,然后按如下方式操作:
x_{\text {pixel }} \\
y_{\text {pixel }} \\
z_{\text {canonical }} \\
1
\end{array}\right]=\left(\mathbf{M}_{\mathrm{vp}} \mathbf{M}_{\text {orth }}\right)\left[\begin{array}{c}
x \\
y \\
z \\
1
\end{array}\right]
z坐标现在将处于\left [ -1,1 \right ]。我们现在没有利用这一点,但是当我们研究z缓冲区算法时,它会很有用。因此,使用端点a_{i}和b_{i}绘制多条3D线的代码变得既简单又高效:
见原书伪代码
7.1.3 相机变换
我们希望能够在3D中更改视点,并朝任何方向查看。有许多约定用于指定相机的位置和方向。我们将使用以下一种(见图7.6):
- 眼睛的位置e
- 注视的方向g
- 相机的向上向量t
眼睛的位置是指眼睛“从什么地方看”的位置。如果认为图形是一种摄影过程,那么它就是镜头的中心。注视方向是观察者注视方向上的向量。相机的向上向量是平面中的任何向量,该向量将观察者的头部平分为左右两半,并为站在地面上的人指向“天空”。这些向量为我们提供了足够的信息,以建立以e为原点和以uvw为轴的坐标系,使用第2.4.7节的构造:
\mathbf{w}=-\frac{\mathbf{g}}{\|\mathbf{g}\|} \\
\mathbf{u}=\frac{\mathbf{t} \times \mathbf{w}}{\|\mathbf{t} \times \mathbf{w}\|} \\
\mathbf{v}=\mathbf{w} \times \mathbf{u}
\end{array}
如果我们希望变换的所有点都存储在原点e和基向量u、v和w的坐标系中,我们的工作就完成了。但如图7.7所示,模型坐标存储在标准(或世界)原点o和x、y和z轴上。为了使用我们已经开发的机器,我们只需要将要绘制的线段端点的坐标从xyz坐标转换为uvw坐标。第6.5节讨论了这种转换,实现这种转换的矩阵是相机坐标系的标准基矩阵:
\mathbf{u} & \mathbf{v} & \mathbf{w} & \mathbf{e} \\
0 & 0 & 0 & 1
\end{array}\right]^{-1}=\left[\begin{array}{cccc}
x_{u} & y_{u} & z_{u} & 0 \\
x_{v} & y_{v} & z_{v} & 0 \\
x_{w} & y_{w} & z_{w} & 0 \\
0 & 0 & 0 & 1
\end{array}\right]\left[\begin{array}{cccc}
1 & 0 & 0 & -x_{e} \\
0 & 1 & 0 & -y_{e} \\
0 & 0 & 1 & -z_{e} \\
0 & 0 & 0 & 1
\end{array}\right]
或者,我们可以将这种变换看作是先将e移动到原点o,然后将u、v、w与x、y、z对齐。
为了使仅沿z轴的观察算法适用于任何位置和方向的相机,我们只需要将此摄影机变换添加到视口和投影变换的乘积中,以便在投影之前将入射点从世界坐标转换为摄影机坐标:
见原书伪代码
7.2 透视变换
我们把透视图留到最后,因为它需要一点聪明才能使它适合向量和矩阵变换系统,到目前为止,它一直为我们提供很好的服务。为了了解我们需要做什么,让我们看看透视投影变换需要对摄影机空间中的点做什么。回想一下,视点位于原点,相机沿z轴查看。
透视图的关键特性是,屏幕上对象的大小与眼睛在原点向上看负z轴时的1/z成比例。这可以更精确地用图7.8中的几何方程表示:
其中y是点沿y轴的距离,y_{s}是点应在屏幕上绘制的位置。
我们真的希望使用我们为正交投影开发的矩阵来绘制透视图像;然后,我们可以将另一个矩阵乘以合成矩阵,然后使用我们已有的算法。然而,这种类型的变换不能使用仿射变换实现。
我们可以通过仿射变换中使用的齐次坐标机制的简单推广来进行除法。我们使用齐次坐标\left [ x,y,z,1 \right ] ^{T}表示点(x,y,z),其中额外的w始终等于1。这是通过\left [ 0,0,0,1 \right ] ^{T}作为仿射变换矩阵的第四行来保证的。
而不仅仅是将1看作附加的一部分,以强制矩阵乘法来实现转换,我们现在将其定义为x、y和z坐标的分母:齐次向量\left [ x,y,z,w \right ] ^{T}表示点\left [ x/w,y/w,z/w \right ]。当w=1时,这没有区别,但如果我们允许变换矩阵的底行中有任何值,则允许实现更大范围的变换,从而使w采用除1以外的值。
具体地说,线性变换允许我们计算如下表达式:
仿射变换将其扩展到:
将w作为分母进一步扩展了可能性,允许我们计算如下函数:
这可以称为x、y和z的“线性有理函数”。但还有一个额外的约束条件,即转换点的所有坐标的分母都相同:
x^{\prime} &=\frac{a_{1} x+b_{1} y+c_{1} z+d_{1}}{e x+f y+g z+h} \\
y^{\prime} &=\frac{a_{2} x+b_{2} y+c_{2} z+d_{2}}{e x+f y+g z+h} \\
z^{\prime} &=\frac{a_{3} x+b_{3} y+c_{3} z+d_{3}}{e x+f y+g z+h}
\end{aligned}
表示为矩阵变换则为:
\tilde{x} \\
\tilde{y} \\
\tilde{z} \\
\tilde{w}
\end{array}\right]=\left[\begin{array}{cccc}
a_{1} & b_{1} & c_{1} & d_{1} \\
a_{2} & b_{2} & c_{2} & d_{2} \\
a_{3} & b_{3} & c_{3} & d_{3} \\
e & f & g & h
\end{array}\right]\left[\begin{array}{c}
x \\
y \\
z \\
1
\end{array}\right]
和:
像这样的变换称为投影变换。
Example.
矩阵
2 & 0 & -1 \\
0 & 3 & 0 \\
0 & \frac{2}{3} & \frac{1}{3}
\end{array}\right]
表示将单位正方形\left ( \left [ 0,1 \right ] \times \left [ 0,1 \right ] \right )转换为图7.9所示四边形的二维投影变换。
处于正方形右下角的\left ( 1,0 \right )点由齐次向量\left [ 1 \quad 0 \quad 1 \right ] ^{T} 表示,并进行如下变换:
2 & 0 & -1 \\
0 & 3 & 0 \\
0 & \frac{2}{3} & \frac{1}{3}
\end{array}\right]\left[\begin{array}{l}
1 \\
0 \\
1
\end{array}\right]=\left[\begin{array}{l}
1 \\
0 \\
\frac{1}{3}
\end{array}\right]
表示点\left ( 1/\frac{1}{3} , 0/\frac{1}{3} \right )或者\left ( 3,0 \right )。注意,如果我们使用矩阵:
6 & 0 & -3 \\
0 & 9 & 0 \\
0 & 2 & 1
\end{array}\right]
进行替换,结果为\left [ 3 \quad 0 \quad 1 \right ] ^{T},这也可以表示\left ( 3,0 \right )。事实上,任何标量乘以cM都是等价的,分子和分母都是按c缩放的,不会影响结果。
有一种更优雅的方式来表达相同的想法,它避免了专门处理w坐标。在这种方法中3D投影变换只是4D线性变换,额外规定向量的所有标量倍数均指同一点:
上述\sim表示两个齐次向量都描述了空间中的同一点。
Example.
在一维齐次坐标系中,我们使用2个向量表示实线上的点,我们可以使用齐次向量\left [ 1.5 \quad 1 \right ] ^{T}表示点(1.5),或齐次空间中x = 1.5h线上的任何其他点。(见图7.10。)
在二维齐次坐标系中,我们使用3个向量来表示平面中的点,我们可以使用齐次向量\left [ -2;-1;2 \right ] ^{T}表示点(-1,-0.5),或直线x= \alpha \left [ -1\quad -0.5 \quad 1 \right ] ^{T}上的任何其他点。直线上的任何齐次向量都可以映射到直线与平面w=1的交点,以获得其笛卡尔坐标。(见图7.11。)
可以根据需要多次变换齐次向量,而不必担心w坐标的值——事实上,如果在某个中间阶段w坐标为零就可以了。只有当我们需要一个点的普通笛卡尔坐标时,我们才需要将其规范化为一个等效点,该等效点的w=1等于将所有坐标除以w。一旦我们这样做了,我们就可以从齐次向量的前三个分量读取(x,y,z)坐标。
7.3 透视投影
投影变换的机制使得实现透视图所需的z除法变得简单。在图7.8所示的2D示例中,我们可以使用矩阵变换实现透视投影,如下所示:
y_{s} \\
1
\end{array}\right] \sim\left[\begin{array}{lll}
d & 0 & 0 \\
0 & 1 & 0
\end{array}\right]\left[\begin{array}{l}
y \\
z \\
1
\end{array}\right]
上述公式将2D齐次向量\left [ y;z;1 \right ] ^{T}转换为1D齐次向量\left [ dy \quad z \right ] ^{T},它表示1D点(dy/z)(因为它等效于1D齐次向量\left [ dy \quad 1 \right ] ^{T})。
对于3D中的“官方”透视投影矩阵,我们将采用我们通常的惯例,即在原点处的摄影机朝向−z方向,因此点(x,y,z)的距离为−z。与正交投影一样,我们也采用了近平面和远平面的概念,这限制了可以看到的距离范围。在此上下文中,我们将使用近平面作为投影平面,因此图像平面距离为-n。
然后,所需的映射是y_{s} = (n/z)y,对于x也是如此。此转换可通过透视矩阵实现:
n & 0 & 0 & 0 \\
0 & n & 0 & 0 \\
0 & 0 & n+f & -f n \\
0 & 0 & 1 & 0
\end{array}\right]
第一行、第二行和第四行实现透视方程。第三行,如正交矩阵和视口矩阵中所示,旨在将z坐标带到“行驶中”,以便我们以后可以使用它删除隐藏曲面。然而,在透视投影中,额外的非常量分母会阻止我们保留z的值—实际上不可能在让x和y做我们需要它们做的事情的同时保持z不变。相反,对于近平面或远平面上的点,我们选择保持z不变。
有许多矩阵可以用作透视矩阵,所有这些矩阵都会非线性扭曲z坐标。该特定矩阵具有如图7.12和7.13所示的良好特性;它将z=n平面上的点完全独立,并将点留在n=f平面上,同时在x和y维度上适当挤压。矩阵对点(x,y,z)的影响为:
x \\
y \\
z \\
1
\end{array}\right]=\left[\begin{array}{c}
n x \\
n y \\
(n+f) z-f n \\
z
\end{array}\right] \sim\left[\begin{array}{c}
\frac{n x}{z} \\
\frac{n y}{z} \\
n+f-\frac{f n}{z} \\
1
\end{array}\right] .
正如您所看到的,x和y是按除以z的比例缩放的。因为n和z(视图体积内)都是负数,所以x和y中没有“翻转”。虽然不明显(参见本章末尾的练习),但变换还保留了z=n和z=f之间z值的相对顺序,允许我们在应用此矩阵后进行深度排序。这些是之后做隐藏面曲面消除的重要信息。
有时,我们会想要取P的逆,例如,将屏幕坐标加上z带回原始空间,就像我们可能想要拾取的那样。矩阵P的逆为:
\frac{1}{n} & 0 & 0 & 0 \\
0 & \frac{1}{n} & 0 & 0 \\
0 & 0 & 0 & 1 \\
0 & 0 & -\frac{1}{f n} & \frac{n+f}{f n}
\end{array}\right]
由于齐次向量乘以标量不会改变其含义,因此对齐次向量进行运算的矩阵也是如此。因此,我们可以通过乘以nf,以更漂亮的形式写出逆矩阵:
f & 0 & 0 & 0 \\
0 & f & 0 & 0 \\
0 & 0 & 0 & f n \\
0 & 0 & -1 & n+f
\end{array}\right]
在等式(7.3)中正交投影矩阵M_{orth}的上下文中,透视矩阵仅将透视视图体积(其形状类似于棱锥体的切片或平截头体)映射到正交视图体积(其为轴对齐的长方体)。透视矩阵的美妙之处在于,一旦我们应用它,我们就可以使用正交变换来获得标准视图体积。因此,所有的正交机制都适用,我们所添加的只是一个矩阵和除以w。同样令人振奋的是,我们没有“浪费”我们的四乘四矩阵的最后一行。
将P与M_{orth}连接在一起会产生透视投影矩阵,
然而,一个问题是:如何确定透视图的l、r、b、t?它们确定了我们观察的“窗口”。由于透视矩阵不会改变(z=n)平面上x和y的值,因此我们可以在该平面上指定(l,r,b,t)。
为了将透视矩阵集成到我们的正交基础结构中,我们只需将M_{orth}替换为M_{per},即在应用相机矩阵M_{cam}之后但在正交投影之前插入透视矩阵P。因此,透视视图的全套矩阵是:
算法为:
参见原书伪代码
注意,除了附加矩阵之外,唯一的变化是除以齐次坐标w。
相乘以后,矩阵M_{per}如下所示:
\frac{2 n}{r-l} & 0 & \frac{l+r}{l-r} & 0 \\
0 & \frac{2 n}{t-b} & \frac{b+t}{b-t} & 0 \\
0 & 0 & \frac{f+n}{n-f} & \frac{2 f n}{f-n} \\
0 & 0 & 1 & 0
\end{array}\right]
这种或类似的矩阵经常出现在文档中,当人们意识到它们通常是几个简单矩阵的乘积时,它们就不那么神秘了。
Example.
许多API,如OpenGL使用与本文所示相同的视图体积。它们通常还让用户指定n和f的绝对值。OpenGL的投影矩阵是:
\frac{2|n|}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\
0 & \frac{2|n|}{t-b} & \frac{t+b}{t-b} & 0 \\
0 & 0 & \frac{|n|+|f|}{|n|-|f|} & \frac{2|f||n|}{|n|-|f|} \\
0 & 0 & -1 & 0
\end{array}\right]
其他的API通常将n设置为0,f设置为1。Blinn建议将规范视图体积设置为\left [ 0,1 \right ] ^{3}以提高效率。所有这些决策都会稍微改变投影矩阵的大小。
7.4 透视变换的一些性质
透视变换的一个重要特性是,它将线转换为线,将平面转换为平面。此外,它将视图体积中的线段转换为标准体积中的线段。要看到这一点,请考虑线段:
当被4×4矩阵M变换时,它是一个具有多种变化的齐次坐标的点:
这齐次化的3D线段为:
如果方程(7.6)可以改写为:
然后所有齐次化的点都位于一条三维直线上。参考等式(7.6),则:
结果还表明,线段确实映射到保留点顺序的线段(练习8),即它们不会被重新排序或“撕裂”。
将线段转换为线段的变换的副产品是将三角形的边和顶点转换为另一个三角形的边和顶点。因此,它可以将三角形转换到三角形,平面转换到平面。
7.5 视野
虽然我们可以使用(l、r、b、t)和n值指定任何窗口,但有时我们希望有一个更简单的系统,即从窗口的中心查看。这意味着:
l = -r \\
b = -t
\end{aligned}
如果我们还添加了像素为正方形的约束,即图像中没有形状失真,则r与t的比率必须与水平像素数与垂直像素数的比率相同:
一旦n_{x}和n_{y}被指定,就只剩下一个自由度。通常使用图7.14中显示为\theta的视野进行设置。这有时被称为垂直视野,以区分左右两侧之间的角度或对角之间的角度。从图中我们可以看出:
如果指定了n和\theta,那么我们可以导出t并使用代码来实现更一般的查看系统。在某些系统中,n的值被硬编码为某个合理的值,因此我们的自由度减少了一个。
常见问题
- 正交投影在实践中有用吗?
它在相对长度判断应用中很有用。它还可以简化透视图,因为透视图在某些医学可视化应用程序中过于昂贵。
- 我在透视图中绘制的球体看起来像椭圆形。这是bug吗?
不,这是正确的行为。如果将眼睛放置在虚拟查看器相对于视口相对于屏幕的相同相对位置,则这些椭圆将看起来像圆,因为它们本身是以一定角度查看的。
- 透视矩阵是否以反向顺序将负z值转换为正z值?那不会引起麻烦吗?
是的,变换z的方程为:
所以z=+ \in转换为z^{\prime} = -\infty,z=- \in转换为z^{\prime} = +\infty。因此,跨越z=0的任何线段都将被“撕裂”,尽管所有点都将投影到适当的屏幕位置。当所有对象都包含在视图范围中时,此撕裂则不会发生。这通常可以通过剪切对象到视图体来保证。然而,如第8章所述,撕裂现象使剪切本身变得更加复杂。
- 透视矩阵更改齐次坐标的值。这难道不会使移动和缩放变换不再正常工作吗?
将一个平移应用到我们已经得到的齐次点:
其他变换也有类似的效果(请参见练习5)。
注意点
大多数关于查看矩阵的讨论都基于实时渲染中的信息。
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:书籍翻译 – Fundamentals of Computer Graphics, Fourth Edition,第7章 Viewing中文翻译
原文链接:https://www.stubbornhuang.com/1896/
发布于:2022年01月08日 14:18:07
修改于:2023年06月26日 20:50:30
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论
52