Games101-Cp2-Rasterization

发布时间 2023-03-24 12:21:54作者: xkyl

所谓光栅化就是在屏幕上画出对应该显示的像素值。

采样(Sampling)

光栅化最简单的方法就是采样,采样就是对连续函数离散化的过程。如:在屏幕空间中定义的三角形,采样过程就是通过自变量像素中心判断是否在三角形内部,在则取true,反之则取false。

\[inside(t, x, y) = \begin{cases} 1, & \mbox{if } point(x, y) in triangle t \\ 0, & \mbox{if } otherwise \end{cases} \]

而是否位于三角形内则通过叉乘,顺/逆时针取边向量与点(x, y)和边向量的起点相减的向量进行叉乘,若结果全正/负,则在三角形内。其中若正好在边界(Edge Case)则根据实际情况自己定义。

包围盒(Bounding Box)

对三角形所在的范围进行划定,减少像素点判断,加速光栅化。其中的方法虎书应该有涉及,后续补充。

采样所带来的错误(Sampling Artifacts)

1.锯齿(Jaggies),也称为走样(aliasing),上述采样中单纯根据是否在三角形内部的条件,会导致在边界上有明显锯齿。
2.摩尔纹(Molre Effect),当去掉图片的奇数行列时导致的错误。

3.车轮现象(Wegon wheel)
etc.

反走样(Antialiasing)方法和分析

反走样的方法是在采样之前先进行模糊(Blur)提前过滤。如对三角形进行模糊后再采样,对应三角形的边界会按照权重取之,则此时整个三角形像素值不全为0或1.
注意先进行采样后模糊(Blurred Aliasing)的操作效果不好,后面具体分析。
本质上产生走样的原因是信号变化过快(频率过高),而采样信号的频率过慢导致的。
我们需要先介绍关于傅里叶变换相关的知识,傅里叶变换用于将时域变换到频域,对应逆变换则相反。
频域和时域分别用来描述信号与频率和时间之间的关系。其中周期信号可以根据傅里叶级数展开成正弦、余弦和常数项之和分解成不同的信号,展开的信号可以按频率从低到高排列。而对应采样信号的频率低时,将无法还原出原始高频信号。

频谱图是对图像进行傅里叶变换后由时域变换到频域得到的图像,图像中心为低频,外围为高频。
滤波=卷积=平均
我们依次介绍上述三种操作,首先滤波(Filtering)是去除特定频率的信号,常见有高通滤波、低通滤波。高通滤波将信号中高频信号保留,可以得到图像边界,低通滤波将信号中低频信号保留,可以得到模糊图像。
卷积(Convoltion)则是取一个过滤窗对一个过滤窗大小的图像进行依次平均,得到新的图像。同时当过滤窗越大,信号频率越低。在时域上卷积相当于在频率上相乘。

而走样就是当采用相同的频率的采样信号无法区分不同频率的原始信号产生的。采样信号通过傅里叶变换得到冲激信号,与原始信号在时域上卷积,相当于在频域上相乘,即采样在频域上复制原始信号频谱。这样当采样信号频率低,复制的频谱则会重叠出现频谱混叠的现象,走样就出现了。

为了去除走样的现象,我们可以先进行低通滤波也就是模糊操作之后再进行采样。在频域上削去高频信号就可以避免上述频谱混叠的现象,也是反走样方法的频域分析。

于是根据上述分析我们减少采样错误的方法主要有两个. 1.增加采样信号频率2.反走样先模糊后采样。对应反走样的操作则是先对图像进行卷积后采样。

抗锯齿方法MSAA、FXAA、TAA

因为计算边界的像素值如果单纯根据是否在三角形内则会出现锯齿,但计算边界像素在模糊区域的像素值不易,我们提出一种近似反走样的方法MSAA,其本质实际上是一种模糊操作,并没有提高采样率。我们可以将一个像素分成\(N*N\)的采样点,判断是否在三角形中,再加权得到对应的像素值。

其他FXAA、TAA后续虎书再具体涉及。

作业2的灵光一现

作业2的具体内容就不多做分析了,包含了光栅化的具体过程,主要提我对是否在三角形内判断的方法,因为顺/逆时针取边向量与点(x, y)和边向量的起点相减的向量进行叉乘,结果要求同正/负。我想到了位操作的方法。

    Vector3f p1{_v[0].x(),_v[0].y(), 0}, p2{_v[1].x(),_v[1].y(), 0}, p3{_v[2].x(),_v[2].y(), 0};
    Vector3f point{x, y, 0};
    int check = 0;
    check <<= 1;
    check += ((p2-p1).cross(point-p1).z() > 0);
    check <<= 1;
    check += ((p3-p2).cross(point-p2).z() > 0);
    check <<= 1;
    check += ((p1-p3).cross(point-p3).z() > 0);

    if(check == 7 || check == 0)
        return true;
    return false;

就到这里了,后面的内容要加快速度了。感谢你看到这里。Cheers!