GAMES101:现代计算机图形学入门-笔记-02
这节课我们来到了光栅化的部分(Rasterization)
话不多说,开干。
光栅化
书接上文:

MVP之后该干嘛呢?我们得到了一个三维坐标范围在[-1,1]的立方体。

那当然是要把这个立方体“丢”在屏幕上了,那么我们首先要定义一下所谓的屏幕是什么。
屏幕是一个由像素组成的二维的数组(如我们所谓的分辨率1920*1080就是指该显示器的屏幕有1920*1080个像素组成),而光栅化的意思就是把某样东西显示在屏幕上的意思,至于像素的定义目前认为就是一个最小的颜色的色块(由R,G,B三种颜色组合而成)。
接下来是关于屏幕空间的定义:

没啥好说的,一些约定俗成的东西而不是严格定义。

现在要把x,y都限定在[-1,1]的一个二维图像(z轴暂时不用管)丢在这个屏幕空间合适的位置上,我们需要:

如图所示,将原本在[-1,1]的点x轴乘以宽度的一半与高度的一半(为什么是一半?因为[-1,1]的长度是2),然后再整体平移(宽度/2,高度/2)(原本是定位在(0,0),现在得在屏幕空间的第一象限的中心)。
那么以上的变换一般称为视口变换。
接着是激动人心引人入胜的显示器介绍环节:

示波器,应该没有人不知道。

介绍了一下隔行扫描,简单地说就是一副图像被扫描两次,第一次只有奇数行,第二次只有偶数行。这是比较早期的处理方式,在当时的时代有其好处,但是缺点就是会有鬼影什么的。

这个是帧缓冲,这个我不是很了解,视频里也是说把这个当成屏幕上显示的是内存的一部分就行了,还拿红白机做举例。

液晶显示器,原理是通过液晶的扭曲来影响光的偏振,从而达到想要的视觉效果。

LED,发光二极管,要么不发光要么发光。
书又归正传了,现在让我们来思考,为什么屏幕上显示的东西总是被拆分成三角形?

三角形是图形学中运用最多的形状,因为他是最简单的多边形(再简单就退化成线段),具有稳定性,任何图形都可以被拆成三角形,三角形一定是平面的(三角形无法通过对折等方法变成立方体,而四边形可以)三角形的内外定义很清晰等等。

接下来我们需要做的就是学习如何判断像素和三角形的关系。
How?答案就是:
采样

如何理解采样呢?如果你的大学专业和我一样,或者你对一些音乐上的制作有所了解,那么这个词你都不会陌生。课件里用了一段伪代码来表示这个意思,就是给出一个采样的函数,然后不断遍历所有的x,然后得到所有的f(x)。

那来到问题本身,我们要采样的其实就是三角形所遮挡的像素的中心点是否在三角形内部。

我们可以这样定义采样的函数。

当然如果是每一个三角形都需要遍历所有的像素点的话运算效率过低,所以引入了bounding box的概念:找到这个三角形的所有点的x轴的最小值和最大值以及y轴的最小值与最大值,构成一个方形的包围盒,显然三角形不可能超出这个范围,我们只需遍历这里面的像素点即可。



显然,我们对这个像素空间的采样率严重不足,这样就会导致我们映射出来的图像与实际的原本图像差异过大,也就是会造成我们俗称的:锯齿(Aliasing)。
锯齿也称走样,如果学过信号与系统的也把这个叫做混叠。如何对抗可恶的混叠现象呢?迫在眉睫,我们立即行动!
采样作为光栅化的关键,往往也伴随着一些问题:
锯齿

摩尔纹

车轮效应(人眼跟不上采样的速度产生错觉)

而这些现象都可以归咎于一个原因:

采样的速度跟不上信号变化的速度,也就是说:天下武功唯快不破,你以为你的采样速度已经很快了,但是还是无法完全跟上信号变换的速度,于是产生了这些artifacts,也就是所谓的瑕疵。
那么,我们要如何
抗走样
接下来就是我们信通专业本科学过的知识了:

在采样前先模糊,又或者叫做,滤波后采样。

原先的采样中,像素点不是纯红就是白的。

提到了两个点:第一是采样定理:

第二点是在边缘形成了一些具有过渡颜色的像素,而不是之前的纯红。那这样做的好处是?


从上下两图可以看出:是的,他就变成了一坨糊糊的东西了!虽然放大看依然辣眼,但是从整体上看的话,会发现养眼不少。
感觉似乎与信号处理中低通滤波有点相像,但是anyway,算是牺牲掉细节的准确性来保证整体的协调性的做法。
傅里叶变换
接下来就是一些理论的复习了(通信原理还在追我)


傅里叶级数展开:任何一个函数都可以用一个正弦函数与余弦函数的集合来表示。

傅里叶变换:通过一个特定的公式变换,我们可以将一个时域的信号转化到频域去,并且也可以逆变换回原来的式子(实现时域与频域的互相转换)。

伴随着频率的增高,采样率的需求也在增大。

而采样率的不足,就会导致哪怕如图的蓝色曲线与黑色曲线两张外观完全不同的信号,采样点却完全一致,从而导致了走样,也就是"aliases";

滤波:舍弃一些特定频率的部分。

如果在图片上使用傅里叶变换就能看出差异,左边是照片(时域图),右边是傅里叶变换后的频域图(低频指图片色彩变化少的部分,高频指图片色彩变换多的部分),不难看出大多数图片的信息(也就是色彩的变换)都集中在低频区域,也就是说这张照片有着更多的大片色彩相同的部分。
睁大眼睛,接下来就是见证奇迹的时刻:

首先是高通滤波,顾名思义就是只有高频才可以通过输出的滤波器,在图片上显示的就是我们的低频部分变成了全黑而只留下了一些比较细节(高频)的部分如头发丝,衣服的线条等。

然后是低通滤波,能够看到除了中心的低频部分,图片上显示出来的样子就是整体图片色彩变换较多的部分都消失了,于是图片更像一坨(无贬义,不知道怎么形容)。

带通滤波:选定特定的频段可以通过滤波,如上图所示,去除了最低频与最高频的部分,会更突出边界(就是图片中周围频率发生较大变化的一部分)与低频之间的部分(因为去掉了变化最明显的部分与变化最不明显的部分,就突出了之间的部分)。
卷积

滤波=卷积=平均(加权)

如图所示,如果滤波是这样的一个可滑动的窗口,那么信号通过滤波后就得到了一个新的输出。接着滤波会一格一格地向右移动。


卷积定理,一句话,时域相乘等于频域卷积,时域卷积等于频域相乘。

卷积定理更直观的表示,时域的图片卷积一个1/9的全1的3*3矩阵等同于频域上乘以一个3*3卷积核来到频域后的图得到的效果的傅里叶变换。

用之前的卷积知识来看这样一个卷积核:等同于从最左上角开始,每九个像素都要依次乘以1之后再相加到某一个像素上去,那么为了保证卷积前后的灰度值(色彩的值)总值不变,需要在外面乘以1/9,也就是所谓的归一化。
从上图的卷积核的频域图也能看出,这个卷积核主要提取的信息都集中在低频段(图像中心),那么就可以把这个卷积核视作一个低通滤波器。


当这个卷积核的尺寸更大(比如21*21),那么在卷积的过程中要每21个像素输出一个结果,于是损失的信息更多,也就是更模糊,那么对应在频域上也就是更“低通”,也就是截止频率更低。也可以往另一个极端想:如果这个卷积核的核超级小,小到了1,那就代表:没有滤波,那么这张图片就没有变化。

采样就是在重复频域的内容:

时域上让一个信号与一个周期冲激函数相乘等效于采样,在频域上的效果如图,等效于周期冲激函数的频域图乘以该信号的频域图,出现了类似于不同频率复制粘贴的效果。

而走样也就是信号的频域混叠(我之前其实已经说过了)

要如何减少走样呢?
提高采样率以及反走样(滤波后采样)。

滤波在频域上的贡献如图:削去可能导致频域混叠的频段。
在实际的操作中,我们往往会对每一个像素都进行卷积。

可以产生这样的结果:

原本一个像素里只有非黑即白,但是滤波后,整个像素会变成统一的灰色,且灰色中黑色与白色的比例会与之前像素中黑白的值的比例相同(就像把原本像素中黑色的部分匀到了整个像素一样)。

MSAA(Multi-Sample Anti-Aliasing)
MSAA的思路比较的简单,就是:

原来三角形覆盖的像素点里只有一个中心,现在我们将屏幕上的每个像素都分成四份,判断每个大像素里有多少个小像素在三角形内,然后根据小像素在三角形中的数量来给值。


这里的误区是:MSAA并不是真的提高了屏幕的分辨率(对每个像素内部进行划分,但是每个像素的索引没变)来提高了采样率,事实上,MSAA只是一种对模糊操作的优化,我们后续仍然要进行采样并且回归正常的像素中心点。事实上,打开我们如今的游戏,大多数抗锯齿的操作选项的原理基本就是MSAA,这样可以显著提升画面的表现能力,但是也会带来巨大的计算负担。

以上是一些别的抗锯齿的方法,大家要是感兴趣自行了解吧~
总结
第二节课开始信息量大大增加且内容难度开始提升,也有部分原因是不再全是我们之前有所了解的内容。紧接着上节课的内容,我们成功将一个三维图像转换成二维图像之后,我们就要考虑如何在我们的屏幕空间上将这个二维图像显示出来:即光栅化。采样是我们的光栅化最重要的步骤之一,而采样总是伴随着一些问题如锯齿,摩尔纹等,归根到底就是采样率不够,采样速率跟不上信号变化速度的原因,而这几乎是无法完全避免的。解决方法主要集中在两个大方向:提高采样率与反走样,反走样更多的就是对我们的图像在采样前进行一些预处理如模糊,如今天依然在用的MSAA等,一定程度上可以提高图像最终的质量但也会带来更大的计算负担,而像更有前景的思路方法如DLSS目前也仍在发展中,敬请期待。
作业
作业只要求我们修改model矩阵与projection矩阵即可,结合我们所学的知识,找到之前绕某轴旋转的旋转矩阵,将角度转化成弧度制,填写矩阵即可得到旋转矩阵(model矩阵),然后关于投影矩阵的话要复杂一点,我们要构造正交投影的矩阵与透视投影的矩阵,分别相乘,一个需要移动至中心,所以只需移动坐标,一个需要根据相似三角形的定理计算出新的z轴上的距离和h,具体如下,做好了之后相乘并在cmake运行即可。要说还是opencv库的配置比较麻烦QAQ。
Eigen::Matrix4f get_model_matrix(float rotation_angle)//模型变换矩阵
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the model matrix for rotating the triangle around the Z axis.
// Then return it.
Eigen::Matrix4f rotation;
double fangle = rotation_angle / 180 * MY_PI;//角度转弧度,便于计算
rotation << cos(fangle), -sin(fangle), 0, 0,
sin(fangle), cos(fangle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;//模型旋转矩阵(绕z轴)
model = rotation * model;
return model;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)//投影变换矩阵
{
// Students will implement this function
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the projection matrix for the given parameters.
// Then return it.
Eigen::Matrix4f proj, ortho;
proj << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;//透视投影矩阵
double w, h, z;
h = zNear * tan(eye_fov / 2) * 2;
w = h * aspect_ratio;
z = zFar - zNear;
ortho << 2 / w, 0, 0, 0,
0, 2 / h, 0, 0,
0, 0, 2 / z, -(zFar+zNear) / 2,
0, 0, 0, 1;//正交投影矩阵,因为在观测投影时x0y平面视角默认是中心,所以这里的正交投影就不用平移x和y了
projection = ortho * proj * projection;
return projection;
}

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)