8.1 采样

8.1.1 采样的定义

  • 采样是利用一些方法(函数)对一个非离散的对象进行映射,得到一组离散的值。
  • 实际上可以理解为本身一张图片或者一个视频拍摄的客观物体是连续的,而摄像机将其捕捉下来,就是将其分为离散的对象的过程。这就是所谓采样。

  • 在后续学习Shader过程中,我们会遇到各种各样的贴图,而往往我们会使用tex2D函数来对其进行采样(Unity中),这个过程就是将一张贴图真正变为像素的过程。
  • image.png
    8.1.1 视频实际上也是采样得到的

8.1.2 采样的问题

  • 采样会出现一些问题,图形学中我们将其称为“Artifacts”瑕疵。例如我们上节课遗留下来的,三角形映射在画面上产生的锯齿问题就是其中一种
  • image.png
    8.1.2 采样中的“瑕疵“
  • 其他问题比如摩尔纹,这是一种因为像素不对齐而导致的差异,比如将一张图像的奇数行和奇数列去掉,但其依然呈现出原本的大小,此时就会产生摩尔纹。

  • 那么综上所述其中有什么规律呢?是什么导致了采样出现了问题呢?其中的最根本的原因就是,采样的对象变化的太快了!这如何理解?比如上面的这个图像,它本身是连续的,因此在实际的内存空间中,变化也是连续的,但如果映射到屏幕像素上,因为我们要填充像素,所以需要使用我们上节课提到的模式,这种模式实际上就是一种迟钝的反应,可能实际数值变化了好几个单位,采样值才变化一点,这就导致了根本的问题。接下来我们看如何解决。

8.1.3 反走样基础

  • 如何进行反走样?
  • 其中一种方法是,先对图像源进行模糊,然后再进行采样操作。但这个顺序不能更改。
  • image.png
    8.1.3 先对对象模糊再采样的方法
  • 这时你一定会问,凭什么,为什么会用这种方法,而最后为啥又不能更改这个顺序呢?这就涉及到信号处理的基础问题了,我们来看。

  • 频域与频率
  • 频域是描述频率变化的坐标系
  • 我们可以通过一些数学方法(傅里叶展开)将任何一个函数通过简单的不同频率的正弦波和余弦波来表示:
  • Pasted image 20240528103614.png
    8.1.3.1 近似表示一个函数

8.1.4 走样的再定义

  • 现在我们可以通过波来解释为何会走样了,我们采样的频率是固定的,但对于不同的波形采样的结果会产生差异,对变化越剧烈的波采样结果差异越大,就像下面这样:
  • image.png
    8.1.4 在波形上的走样的分析

8.1.5 傅里叶变换与滤波

  • 傅里叶变换看起来似乎很困难,但实际上,我们不需要知道他具体是什么样的。只用知道最后它带给我们了一个结果,我们可以通过这个结果来观察到一些内容,当然这个方式也是可以相互转换的。
  • Pasted image 20240528104651.png
    8.1.5 傅里叶变换

  • 滤波,滤波实际上就是对上面那一幅图案进行一些操作。因为实际上我们得到的是相互对应的波形图。因此如果我们对其进行一系列操作,实际上会直接反应到图片上,这就是对图像进行的处理操作。
  • Pasted image 20240528105134.png
    8.1.5.1 高通滤波
  • 上面这个图片进行的操作实际上就是过滤掉低频部分,也就是相对应的中间的部分,而剩下来的就是高频部分。

  • 我们可以在PS中做一些实验,看看频域与图像的关系:
  • image.png
    8.1.5.2 色阶映射到频域上的体现
  • 左侧这张图片就是实域,而右侧调节的对象就是频域,通过调节频域的变化与占比来调节实域的颜色。

  • 如果细心的同学可能会发现,得到的效果是图像的边缘。边缘和高频又何关系呢?我们可以这么理解,高频区域实际上就是图像变化幅度最大的区域,也就是边界,因此如果我们过滤掉低频区域,那么最后剩下的就是边界了。
  • 这些操作都是涉及到图像处理方面的知识,这里只是跟大家分享概念。后续UnityShader部分会实现一个类似的效果。

8.1.6 卷积与滤波

  • 卷积是什么,卷积实际上就是一种滤波操作,它在数学上是一种平均计算,这两者的内在机理是什么?滤波是对数值的操作,过滤掉特定数值的波,而卷积的平均同样也是对数值进行的平均

  • Pasted image 20240528105806.png
    8.1.6 卷积的操作
  • 通过上面这张图片我们可以发现,它是上述的这些数字通过一个固定的值进行取平均的运算最后得到一个结果,这个过程就叫做卷积操作。
  • 这些固定的数值就叫做卷积核。

  • 对于一个实域图像,进行卷积操作,实际上我们也可以将其转化为频域上的图形进行操作,这个操作同样成立,而这种操作则是用乘除法实现的,殊途同归。
  • Pasted image 20240528110301.png
    8.1.6.1 实域上的卷积操作
  • 所以在频域上的乘积就等于实域上的卷积。

8.1.7 采样的重定义

  • 一个函数进行傅里叶变换后得到的结果在频域上体现出来的就是一个峰值:
  • Pasted image 20240528111414.png
    8.1.7 函数在频域上的体现
  • 然后,我们使用名为冲击函数的函数对其进行采样,最后得到的就是一系列采样的值,因此我们可以定义采样就是重复原始数据的过程。
  • Pasted image 20240528111437.png
    8.1.7.1 采样的本质就是重复数据

  • 现在我们可以来定义走样了,走样就是重复的频率太慢了,冲击函数的频率慢了,因此最后得到的结果就会重复,这样就发生了走样。
  • Pasted image 20240528111652.png
    8.1.7.2 走样的原理
  • 现在我们已经得到了走样的最终定义了,那就是在重复采样数据的过程中,因为采样频率慢而导致采样结果相互干涉,导致出现走样,下面我们来重新来审视如何反走样。

8.2 反走样

8.2.1 反走样的方式

  • 1.提升屏幕自身的分别率,增加采样频率,但这并不是解决方案,因为我们无法改变物理的显示状态。
  • 2.先对原始信号进行操作,避免发生重叠。

  • 我们来看一下第二个方法,之前我们提到最后得到的对实域的采样得到的频域上的结果,我们可以将其高频部分去掉,这本身就是将其进行模糊,因为低通滤波本身就是模糊操作,其图像如下,我们会发现在原本的采样率下,它们不重叠了。

  • Pasted image 20240528111944.png
    8.2.1 去除高频信号后的结果
  • 现在我们可以回答原本的问题了,为什么我们要先模糊再采样,原因就是我们可以避免信号的重叠,从而避免走样。

8.2.2 抗锯齿方式

  • MSAA
    • MSAA这种方式实际上是一种取巧的方式,每一个像素中抽象出数个像素点,我们对其采样后即可得到更细致的采样结果,得到这个结果后,我们就将其进行平均,最后得到这个像素的采样值,这其实模拟了卷积的过程。
    • Pasted image 20240528112415.png
      `8.2.3 MSAA的实现示意

  • FXAA
  • FXAA是一种快速进行抗锯齿的操作,只需要很简单的操作就能得到结果,更注重与性能而非完美的效果。缺点是在移动时会发生闪烁。
  • 详情请参考此文:
  • https://zhuanlan.zhihu.com/p/431384101

  • DLSS
  • DLSS是一种基于机器学习的AI生成算法,通过走样的画面反推丢失的细节,但需要用到一些特殊单元,目前只有有限的显卡支持。

参考资料