Part 2 光栅化成像
约 4139 字大约 14 分钟
2025-03-11
1 模型视图变换
成像中必不可少的就是相机。相机位置可以由三个属性定义:
- 位置 e
- 视线方向 g^
- 向上方向 t^
相机的位置自然是自由的,但是对于计算成像来说就不太舒服了。你可能遇到乱七八糟的坐标和方向,对人类和计算机都不太友好。
因此我们通常保持物体模型和相机的相对位置不变,而整体移动模型和相机,使相机达到标准位置。在这种情况下,相机应该位于原点,视线方向为z轴负方向,向上方向为y轴正方向。
将一个任意位置、任意方向的相机移动到标准位置,需要经过以下步骤:
- 将相机移动到原点
Tview=100001000010−xe−ye−ze1
- 将g^旋转到−z轴
- 将t^旋转到y轴
- 将g^×t^旋转到x轴
理论很简单,但是将任意向量旋转至坐标轴上并不容易,因此可以反向思维:
- 将相机移动到原点
- 将x轴旋转到g^×t^
- 将y轴旋转到t^
- 将−z轴旋转到g^
因此有
Rview−1=xg^×t^yg^×t^zg^×t^0xtytzt0x−gy−gz−g00001
容易验证Rview−1正交,于是:
Rview=(Rview−1)−1=(Rview−1)T=xg^×t^xtx−g0yg^×t^yty−g0zg^×t^ztz−g00001
因此模型视图变换矩阵为:
Mview=RviewTview=xg^×t^xtx−g0yg^×t^yty−g0zg^×t^ztz−g00001100001000010−xe−ye−ze1
2 投影变换
2.1 正交投影
正交投影不满足现实生活中“近大远小”的规律,更像是整个场景被“拍”进了平面中。
正交投影可以由下述操作得到:
- 将相机和物体变换至标准位置
- 将物体置于标准立方体中
- 令z=0
当一个边长为2的立方体的中心位于原点,且所有边都与坐标轴平行时,即为标准立方体,也即[−1,1]3。
将任意立方体[l,r]×[b,t]×[f,n]变换到标准立方体分为两步:
- 将立方体中心移动到原点
T=100001000010−2r+l−2t+b−2n+f1
- 将立方体的各条边都缩放至2
R=r−l20000t−b20000n−f200001
最后的变换矩阵为
Mortho=RT
2.2 透视投影
人眼接收到世界的画面就是透视投影。在这种情况下,画面符合“近大远小”的规则,并且所有的投影线都将汇聚于一点。
相机接收到的画面应该是从镜头处发散出去形成一个四棱锥体,再截取远近平面之内的物体,形成一个四棱台。
然后将四棱台“挤压”成一个立方体,剩下的就是正交投影的内容了。
所以透视投影的关键在于将棱台变换为立方体。
我们先沿着x轴方向从正向负观察模型:
n代表近平面到原点的距离,z代表远平面到原点的距离。为了避免混淆,我们将远平面距离用f来表示。
由相似三角形:
y′=fny
x′=fnx
改写为齐次坐标:
x′y′z′1=fnxfny?1=nxny?f=Mpersp→orthoxyz1
我们可以很容易地看出变换矩阵Mpersp→ortho的第 1、2、4 行,但是由于我们不知道z坐标的变换,所以第三行我们写不出来。
nxny?z=n0?00n?000?100?0xyz1
进一步观察模型发现:变换前后,近平面上的点(x,y,n,1)没有发生任何变化;远平面上的点(x,y,f,1)尽管x和y坐标发生改变,但是z坐标没有发生变化。
首先考虑规律 1:变换前后,近平面上的点没有发生任何变化。
xyn1=nxnyn2n=n0?00n?000?100?0xyn1
只看第三行,发现n的变换与x和y无关。(尽管这里没有严谨证明,但是我们先大胆假设一下)
于是:
n2=(00AB)xyn1=An+B
同理由规律 2 可得:
f2=(00AB)fxfyf1=Af+B
联立方程解得:
AB=n+f=−nf
于是
Mpersp→ortho=n0000n0000n+f100−nf0
最终的透视投影矩阵为:
Mpersp=MorthoMpersp→ortho=r−l20000t−b20000n−f200001100001100010−2r+l−2t+b−2n+f1n0000n0000n+f100−nf0
这个矩阵可以将任意一个
- 远近平面垂直于z轴,平行于xOy平面
- 远近平面的边都与x轴和y轴平行
- 远近平面的中心连线平行于z轴
的四棱台变换到标准立方体中。
2.2.3 视场角与长宽比
刚才我们得到了透视投影矩阵,但是其中的参数l,r,b,t无法直接求得。
因此引入视场角fov和长宽比AspectRatio的概念。
由此得到:
tbrl=ntanfov=−t=AspectRatiot=−r
3 视口变换
刚才我们已经将模型通过透视投影转换成为了标准立方体,接下来就要将三维模型显示在二维屏幕上的问题。
首先我们对模型的标准立方体进行正交投影,即令z坐标为0,此时其投影位于平面[−1,1]2内。
然后把[−1,1]2平面映射到屏幕[0,w]×[0,h]中:
Mviewport=2w00002h0000102w2h01
这个变换称为视口变换。
4 光栅化成像
4.1 屏幕与像素
对于图形学来说,屏幕可以抽象为一个像素的二维数组。
像素是一个单色的正方形,其颜色可以由红、绿、蓝三种颜色来表示。
我们使用的屏幕坐标系是以右下角为原点,向右为x轴正方向的右手系,宽高分别为w个像素和h个像素。
像素的坐标用其左下角的点的坐标来代替,因此像素坐标的取值范围为(0,0)到(w−1,h−1)之间的整数格点,像素(x,y)中心坐标为(x+0.5,y+0.5)。
如下图中蓝色像素的坐标可以被表示为(2,1),其中心为(2.5,1.5)。
4.2 三角形的光栅化
光栅化可以理解为把物体显示在屏幕上的过程。
得益于三角形的诸多良好性质,图形学中通常用三角形近似表示复杂物体。因此各种模型的光栅化均等同于为三角形的光栅化。
例如,我们要将下图所示的三角形光栅化到屏幕上,直观上可以认为:中心落在三角形内部的像素将其涂色,而中心落在三角形外部的像素不作处理。
于是我们就完成了一个非常简单的光栅化。
相关信息
向量积可用来判断“左和右”。假设下图中的向量a和b在 xOy平面上,则根据右手定则,a×b 指向z轴正方向,那么我们认为b在a的左侧;同理,b×a指向z轴负方向,那么我们认为a在b的右侧。
进一步,向量积还可判断“内与外”。依次作AP×AB,BP×BC,CP×CA,若以上向量积指向的方向相同,则P点在三角形ABC的内侧,否则在外侧。
提示
在编写程序中,我们采用遍历的形式依次决定像素是否被涂色。这对于高分辨率的屏幕会带来无意义的消耗(每光栅化一个三角形就要遍历所有像素)。因此我们会计算这个三角形的包围盒(Bounding Box),只遍历包围盒中的像素,从而加速光栅化。
5 抗锯齿
到这里,你已经能看出问题了:光栅化得到的图像会形成锯齿。因为像素并不能完全描述原有的三角形。
可以这么简单理解:三角形是连续的,而像素是不连续的。在采样过程中发生了信息丢失。
5.1 采样和走样
现实世界中的信息几乎都是连续的,声音、电磁波、光信号等,它们以特定的频率和波形传播信息。
我们知道,任何一列波形都可以使用傅立叶级数展开成为一系列正弦函数、余弦函数和常数项的线性组合。
例如,下面这个函数可以近似表示一个方波:
f(x)=2A−3π2Acos(3tω)+5π2Acos(5tω)−7π2Acos(7tω)+…
因此下面的分析就以最简单的正弦函数和余弦函数来举例。
我们在学习绘制函数图像的时候学习过描点法,其思想是:在寻找若干个具有代表性的、在函数上的点,并将其一一连接,得到一段折线。这段折线就可以近似描述任何复杂函数。采样点越多,折线和函数越近似;采样点越少,折线越失真。
观察下面这组波,从上到下变化频率依次增加,但是采样点的数目并没有变化。因此第 1 和第 2 列波描点采样后形成的折线能够基本反映波的形状;第 3 列波采样结果开始失真;第 4 和第 5 列波基本完全不能反映原波了。
自然而言得出结论:采样频率应该与真实频率相近。
特殊地,像下面这种情况,即在某个频率采样下,两条频率完全不同的波采样得到的结果是相同的。
显然,符合满足一组采样的波总是不止一条(会有各种各样的波通过各种各样的方式完美穿过你的所有采样点),使得我们想要形状被另一种无关的形状替换。这种情况我们就称之为走样。
摩尔纹就是一种典型的走样。
5.2 频域和滤波
应用傅立叶变换,我们可以画出任意图像的频域图。频域图描述了这幅图像中各个频率的信息的占比。越接近中心,代表频率越低(一般为大块、均匀、变化不明显的区域);越接近外侧,代表频率越高(一般为细小、破碎、变化剧烈的区域)。其占比使用亮度表示,即亮度越高,信息占比越多,反之占比越少。
下面这幅图中低频信息较多,而高频信息较少,因此在频域图呈现出中心亮而周围暗的状态。
我们可以认为屏蔽掉某些频率的波,使得图像变为我们想要的样子。
例如,屏蔽掉所有低频信息,只留下变化剧烈的边界:
屏蔽掉所有高频信息,只留下变化不明显的大块色块:
前者过滤掉所有的低频信息,只允许高频信息通过,我们称之为高通滤波器。后者称为低通滤波器。
当然,为了提取出一些不明显的信息,还有各种各样的滤波器:
5.3 多重采样抗锯齿(MSAA)
经过上面的分析,我们知道了造成走样的原因是因为高频信号不匹配低频采样。既然如此,我们使用低通滤波器处理原图像,再进行采样,就可以解决问题了。
我们通常使用这个低通滤波器处理图像:
91111111111
这个滤波器每次处理一个像素和它周围的八个像素,中心像素的值等于周围八个像素与它自身的平均值.
自然,经过滤波之后,每个像素的值都由原来的 0 和 1 变成了一个浮点数,视觉上就是从“非黑即白”变成了具有灰度的图像。
直白地说,就是三角形覆盖到这个像素的比例。
因此我们直接计算这个比例就好了。但是精确求解三角形覆盖的像素面积比例需要消耗过多的计算资源,因此提出了一种近似方法。
这种方法将一个完整的像素划分为若干个子像素,判断每个子像素的中心是否在三角形内. 统计在三角形内的子像素的比例,即可得到近似值。
下图是将每个像素划分为 4 个子像素,每个像素的取值为 0、0.25、0.5、0.75、1。
这就是**多重采样抗锯齿(MSAA)**的基本思想。
6 深度缓冲
深度缓冲用来解决不同远近、相互遮挡的三角形应如何在屏幕上显示的问题。
类比地说,在作油画时,画家们通常先画远处的物体,然后再一层一层画近处的物体. 这被称为画家算法.
但是对于下面这种情况,画家算法显然不太适用:
为了解决这个问题,引入深度缓冲的概念。
在生成图像时,同步生成两张图像。一张用于记录颜色,一张用于记录深度。
基本思想是,初始化一个和屏幕像素数量相匹配的颜色数组和深度数组,并将深度数组的元素都置为无穷大。
然后遍历所有三角形中的所有像素,如果对应位置的像素的深度小于当前深度数组中所记录的值,就更新深度数组记录的当前像素深度,同时更新颜色数组中记录的当前像素颜色,否则不做操作。
如下图所示。