GAMES101——Leature 06: Rasterization 2 (Antialiasing and Z-Buffering)

1 反走样 Antialiasing

1.1 采样的理论

采样不止可以发生在不同的位置,还可以发生在不同的时间。

我们把一系列的图按照一定的时间放出来,就可以形成一系列的动画。这个动画就可以称为在时间中进行的采样。因为我们其实没有见过连续意义上的动画,我们看到的是所谓很连贯的视频。视频是由一帧一帧的图来组成的,一秒内依次放出 24 帧图像,让大家视觉上会认为是个连续的过程。但是其实它在每个时间点上都是离散的一幅幅图。

Sampling Artifacts:Errors / Mistakes / Inaccuracies in Computer Graphics.

采样会产生不同的问题:

  • 锯齿现象
  • 摩尔纹 (Moire Patterns) 平时拿手机去拍屏幕,也会看到一些扭曲的纹路。
  • 车轮错觉 (Wagon Wheel illusion) 车轮在高速运动的时候,人们会看到车轮在倒着转的错觉(解释)。因为人眼在时间中的采样跟不上轮子运动的速度,就会出现这样的现象。

产生以上问题的本质在于:信号变化的速度太快了,但是采样的速度太慢了,跟不上信号变化的速度。

反走样的理念:采样之前做个模糊操作(滤波)

采样前做个模糊操作,采样的呢就是个模糊的三角形,该什么颜色对应像素就是什么颜色。解决效果还不错。

在这里插入图片描述

但是为什么呢?

① 为什么采样的速度跟不上信号变化的速度就会产生走样现象?

回答:通过频率来分析走样是怎么回事儿

② 为什么需要在采样之前做模糊操作?先采样后做模糊操作出来的效果就不行呢?

接下来就需要用到频域方面的知识了。

频域

傅里叶变换

傅里叶级数展开:任何一个周期函数,都可以把它写成一系列正弦和余弦函数的线性组合,以及一个常数项。(下图中的f(x)式子)

傅里叶级数展开可以描述很多不同的正弦余弦项的和。通过傅里叶级数展开我们可以知道:任何不同的函数都可以分解成不同的频率。(通过下图的右边 4 幅图就可以看出:加入的函数频率越来越高,最终的近似效果也越来越好。也意味着这个最终的图像也可以分解为这么不同的频率。)

在这里插入图片描述

我们给定一个函数,可以让它经过相当相当复杂的操作,变成另外一个函数。并且可以通过逆变换把它变回原来的函数。这就是傅里叶变换和傅里叶的逆变换。

在这里插入图片描述

傅里叶变换就是把函数给变成不同的频率的段,并且我们把这些不同频率的段给显示出来。通过傅里叶变换,我们就可以用来分析函数有着什么样的频率。

下图的五个函数有着不同的频率,从低到高,从上到下。我们用完全相同的采样方法对这些函数进行一次采样。

**得出的最终结论是:**更高频率的函数需要更密的采样。

在这里插入图片描述

通过频率来分析走样是怎么回事儿

用同样的采样方法采样两种频率完全不同的函数,得出的结果却是完全一致,无法区分它们,这样的现象就叫做“走样”。

在这里插入图片描述

滤波 = 去除某些频率内容 = 卷积(=平均)

傅里叶变换可以把函数从时域变到频域,如下图所示。

在这里插入图片描述

卷积:在周围的区域做一个平均。

在这里插入图片描述

时域上,如果想对两个信号进行卷积。其实对应到两个信号各自的频域上,是两个信号的频域的乘积。所以卷积操作和乘积操作是挺接近的。

**卷积定理:**函数的卷积的傅里叶变换是函数傅里叶变换的乘积。卷积定理揭示了时间域与频率域的对应关系。

  • 时域卷积定理:时域内的卷积对于频域内的乘积
    • 两信号在时域的卷积积分 = 在频域中该两信号的傅里叶变换的乘积
  • 频域卷积定理:频域内的卷积对应时域内的乘积
    • 两信号在时域的乘积 = 这两个信号傅里叶变换的卷积除以 2π

通过卷积定理,我们就知道如何做一个卷积:

  • 可以直接拿到一幅图,直接用卷积的滤波器去做一个卷积操作。
  • 也可以把这张图
    • 先做一个傅里叶变换,变到频域上;
    • 后把卷积的滤波器也变到频域上;
    • 两者相乘,得到频域的结果;
    • 然后再将结果傅里叶逆变换为时域上。
采样 = 重复频率内容

假如说我要采样第一个函数a,那就是需要把这个函数变成一系列离散的点,只留下在某些位置上函数的值。这就好像在这个函数上乘以另外一个函数,比如第二个函数c,它只在某些地方上有值,其他地方值为 0。最终相乘的结果就是第三个函数e。

在这里插入图片描述

如何做采样呢?

给一个原始的信号,去乘上第二个这样的函数,就可以得到第三个函数,就是采样的结果。

这是在时域上,如果在频域上呢?已知时域上的乘积对应的就是频域上的卷积。

那么就是b卷积d 得到的结果为 f。

从f 图可以总结出:采样是什么?采样就是在重复原始信号的频谱。

我们就可以理解了“为什么会产生走样现象”这个问题:

因为采样不同的间隔会引起频谱以另外一个不同的间隔移动。走样在频域的角度上来说,就是频谱在搬移的情况下发生了混叠。如下图所示

在这里插入图片描述

1.2 实际的图形学中如何做反走样

方法一:增加采样率

这是一个终极解决方法。但是代价昂贵且可能需要非常高分辨率。

方法二:反走样

先模糊,后采样。也就是先把原始的高频信号拿掉,后采样。原理如下:

在这里插入图片描述

模糊操作

那我们如何进行滤波操作?也就是如何进行模糊处理呢?

先模糊操作,也就是卷积操作,求个平均。

我们期望做到的:通过计算平均像素值进行抗锯齿。在栅格化一个三角形时,函数f(x,y) = inside(triangle,x,y) 的像素区域内的平均值等于该三角形覆盖的像素的面积。

在这里插入图片描述

MSAA

用更多的采样点来反走样。MSAA是上述期望做法的一种近似操作。毕竟算出三角形覆盖每一个像素的面积大小是需要很大计算量的。

MSAA 的思想:认为一个像素内被划分为好多小的像素。假设被划分为 4 x 4 = 16个小像素。每个小的像素假设有个中心,然后判断这些点是否在三角形内,将最终得到的结果平均起来。这个平均后的结果就可被看做三角形对这个像素点覆盖区域的近似。

在这里插入图片描述

MSAA 做的是模糊操作。它绝不是靠提升频率分辨率来增加效果,只是靠增加采样点来更好地近似三角形的覆盖率而已。

MSAA 为增加效果付出的代价?

增加了计算量。一个像素被划分为 4 x 4,那么就是多了 16 倍的计算量。

为了解决这个弊端,后续人们会用更加有效的图案来分布采样点,而且这些采样点有的还会被邻近的复用。

采样操作

采样就很简单,此时一整个像素格子就是一个颜色,直接采样在模糊操作完之后的像素点中心取这个颜色就好。

小结:

1、很重要的另外两种抗锯齿方法

  • FXAA 快速近似抗锯齿。和增加样本数没有任何关系,它是一个图像的后期处理。原理:通过图像匹配的方式找到有锯齿的地方,然后把这些有锯齿的边界给替换成平滑的边界。
  • TAA 简单高效,与时间相关。原理是复用上一帧的信息。

2、超分辨率/超采样

  • 从低分辨率到高分辨率。把小图变成大图,又不想看到锯齿现象。
  • 基本上还是“样本不足”的问题
  • DLSS(深度学习超级采样)

2 可见性与遮挡

2.1 画家算法

原理

先对远处的物体进行光栅化,后逐步对近处的物体进行光删化操作。(画近处的物体时会覆盖掉之前画的远处的一些物体)这样就可以正确地处理遮挡的问题了。

涉及到一个问题

这里涉及到一个问题:当某些物体在深度上存在互相遮挡的情况,此时没有办法定义顺序关系,这样就无法使用画家算法,因为不知道应该按照什么样的顺序画,或者不管先画什么都是错的。如下图所示:

在这里插入图片描述

2.2 问题解决方法:深度缓存/缓冲 z-buffer

思想:在生成最终图像的同时,也会生成一个“只存像素所看到的几何物体最浅的深度信息”的图像。然后利用深度缓存来维护遮挡信息。

  • 每一个像素内存储最浅深度值,即 z 值。

  • 需要额外的深度值缓冲区

    • 帧缓冲区(frame buffer)存储颜色值
    • 深度缓冲区(z缓冲区)存储深度

在这里插入图片描述

之前的讲述是摄像机永远看向z轴的负方向,因此所有的 z 都是负的。以为这 z 值越大距离反而越近,z 值越小,离摄像机的距离越远。

为了简化计算,将之前看到的 z 换一个概念。***我们现在认为,摄像机到物体之间的距离,也就是深度信息,这个值永远都是正的。越小的值表示越近,值越大表示距离越远。***

深度缓存算法思想:

  • 一开始将所有像素记录的深度缓存距离都初始化为无限远,即初始化为无限大的值。
  • 在光栅化的过程中,找到任意一个三角形所覆盖的任意一个像素。
  • 如果新的三角形要画在这个像素内,对于这个像素来说,如果新的 z 值比之前所存储的深度信息还要小,那么就将像素点的颜色值和深度信息值都进行更新。否则,什么都不做。

注意这里并未进行排序,只是在不断求最小值而已。复杂度为O(n)。

该算法有一个很重要的特点:和绘制顺序是没有关系的,只要正确求得最小的深度信息,最后绘制出来的图像就是对的。(前提条件,不出现在同一个像素点上出现相同的深度信息的情况。)

总结:

这是一个非常非常重要的算法,几乎广泛应用在所有的硬件中。每一个像素维护一个深度测试,就可以得到正确的遮挡算法。

之前有提到过 MSAA 的方法,对于这里面的采样点进行深度缓冲的话。意味着也不一定是对每一个像素进行 z-buffer, 也有可能是对采样点~

z-buffer 一定处理不了透明物体的深度,这个需要特殊处理。

绘制透明物体

缓冲区其实就是一张和最终输出图像分辨率一样大的图,每个像素存储了不同的数据。

1)深度缓冲区 z-buffer:每个像素记录了离相机最近的片元的深度。

2)颜色缓冲区 Frame buffer:也叫帧缓冲区,每个像素记录了当前该位置对应的颜色。

1、场景中存在半透明物体的正确渲染

​ 由于通过透明物体是可以看到被其遮挡的物体的,因此对于深度缓存中的深度值就不能直接替换更新,深度值应该是离相机最近的不透明物对应的片元的深度值,否则就会出现透明物体挡住不透明物体的渲染错误。
​ 因此在渲染半透明物体时,需要关闭深度写入,但要保留深度测试。(深度测试的目的在于判断:是否因为遮挡而舍弃当前片元,如果该透明片元在不透明物体后,它被遮挡了,深度值比较大,当然应该被丢弃。当它在不透明物体前,它就不会被丢弃,但是又不会将当前的深度值写入,还是会以不透明物体的深度信息为准。)

​ 同时对于颜色也不可以和以前一样替换更新,而是要做透明度混合,即用帧缓存中已有的颜色和透明片元的透明色混合出一个新的颜色。

场景总存在透明物体时的渲染步骤:

  • 先渲染不透明物体,后渲染透明物体

  • 对透明物体进行排序,然后从远到进的顺序依次渲染。(因为只有这样才能正确地叠上,并混合出正确的颜色)

  • 并开启它们的深度测试,但关闭深度写入。

但此时还存在两个问题:

1)仍然无法解决透明物体相互交叠的问题。

2)透明物体内部如果自身互相交叠也会存在问题。无法判断片元的先后顺序。

2、半透明物体间相互交叠的问题

3、半透明物体的自身交叠问题及双面渲染

关闭背面剔除,将双面渲染的工作分成两个pass,渲染两次:

  • 第一个pass只渲染背面
  • 第二个pass只渲染正面

保证背面总是在正面被渲染之前渲染,从而可以保证正确的深度渲染关系

  • 1
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论 3

打赏作者

程颖不是陈颍是沉吟

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值