抗锯齿

作者: 不决书 | 来源:发表于2023-09-20 07:40 被阅读0次

锯齿边缘(Jagged Edges)的产生和光栅器将顶点数据转化为片段的方式有关


image.png

够清楚看见形成边缘的像素。这种现象被称之为走样(Aliasing)

有很多种抗锯齿(Anti-aliasing,也被称为反走样)的技术能够帮助我们缓解这种现象,从而产生更平滑的边缘。

最开始我们有一种叫做超采样抗锯齿(Super Sample Anti-aliasing, SSAA)的技术,它会使用比正常分辨率更高的分辨率(即超采样)来渲染场景,当图像输出在帧缓冲中更新时,分辨率会被下采样(Downsample)至正常的分辨率。这些额外的分辨率会被用来防止锯齿边缘的产生。

多重采样

image.png

每个像素的中心包含有一个采样点(Sample Point),它会被用来决定这个三角形是否遮盖了某个像素


image.png

多重采样(Multisampling): 重采样所做的正是将单一的采样点变为多个采样点(这也是它名称的由来)。我们不再使用像素中心的单一采样点,取而代之的是以特定图案排列的4个子采样点(Subsample)

image.png

采样点的数量可以是任意的,更多的采样点能带来更精确的遮盖率。

MSAA真正的工作方式是,无论三角形遮盖了多少个子采样点,(每个图元中)每个像素只运行一次片段着色器。片段着色器所使用的顶点数据会插值到每个像素的中心,所得到的结果颜色会被储存在每个被遮盖住的子采样点中。当颜色缓冲的子样本被图元的所有颜色填满时,所有的这些颜色将会在每个像素内部平均化。

image.png image.png

对于每个像素来说,越少的子采样点被三角形所覆盖,那么它受到三角形的影响就越小。三角形的不平滑边缘被稍浅的颜色所包围后,从远处观察时就会显得更加平滑了。

OpenGL中的MSAA

如果我们想要在OpenGL中使用MSAA,我们必须要使用一个能在每个像素中存储大于1个颜色值的颜色缓冲(因为多重采样需要我们为每个采样点都储存一个颜色)。所以,我们需要一个新的缓冲类型,来存储特定数量的多重采样样本,它叫做多重采样缓冲(Multisample Buffer)。
这可以在创建窗口之前调用glfwWindowHint来完成。

  glfwWindowHint(GLFW_SAMPLES, 4);

还需要调用glEnable并启用GL_MULTISAMPLE

  glEnable(GL_MULTISAMPLE);

离屏MSAA

为了创建一个支持储存多个采样点的纹理,我们使用glTexImage2DMultisample来替代glTexImage2D,它的纹理目标是GL_TEXTURE_2D_MULTISAPLE。

  glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
  glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);
  glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

第二个参数设置的是纹理所拥有的样本个数。如果最后一个参数为GL_TRUE,图像将会对每个纹素使用相同的样本位置以及相同数量的子采样点个数。

使用glFramebufferTexture2D将多重采样纹理附加到帧缓冲上

  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0);

多重采样渲染缓冲对象,将glRenderbufferStorage的调用改为glRenderbufferStorageMultisample就可以了

  glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);

渲染到多重采样帧缓冲,多重采样帧缓冲的还原通常是通过glBlitFramebuffer来完成,它能够将一个帧缓冲中的某个区域复制到另一个帧缓冲中,并且将多重采样缓冲还原。

    glBindFramebuffer(GL_READ_FRAMEBUFFER, multisampledFBO);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
  glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);

我们就能够在一个几乎没有锯齿的场景纹理上进行后期处理了。如果施加模糊的核滤镜,看起来将会是这样:


image.png

因为屏幕纹理又变回了一个只有单一采样点的普通纹理,像是边缘检测这样的后期处理滤镜会重新导致锯齿。为了补偿这一问题,你可以之后对纹理进行模糊处理,或者想出你自己的抗锯齿算法。

自定义抗锯齿算法

要想获取每个子样本的颜色值,你需要将纹理uniform采样器设置为sampler2DMS,而不是平常使用的sampler2D:

  // 采样
  uniform sampler2DMS screenTextureMS;
  // 获取每一个采样的样本
  vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3);  // 第4个子样本
 
  // TODO....

相关文章

网友评论

      本文标题:抗锯齿

      本文链接:https://www.haomeiwen.com/subject/ugmovdtx.html