抗锯齿 anti-aliasing

本文来自对learnopengl.com上教程的个人总结

一 概念

有时候OpenGL渲染出来的物体,其边缘会出现锯齿,显得很不丝滑。说白了,使物体在渲染时保持边缘丝滑而不出现锯齿的技术,就叫做抗锯齿。常用的技术是SSAA和MSAA,MSAA是SSAA的演进技术,全称是多采样点抗锯齿 multisampler anti-aliasing,本文主要介绍MSAA.

二 MSAA原理

片段着色器携带顶点数据运行时,将数据插入到像素的中心,MSAA随后使用一个很大的的带有设置了子采样点的深度/模板缓冲去选择哪些子采样点被覆盖,然后将像素按照--覆盖的子采样点数/总采样点数 的比例,混合到默认帧缓冲区framebuffer中,成为输出像素的一部分。

三 用法

1 设置像素的子采样点 subsamples
需要是采样点缓冲区来保存采样点,而GLFW提供了一个多采样点缓冲:
    glfwWindowHint(GLFW_SAMPLES, 4);//每个像素有4个子采样点
当调用glfwWindowHint创建窗口时,每个屏幕坐标缓冲中包含4个子采样点。

2 开启抗锯齿功能
  glEnable(GL_MULTISAMPLE);// OpenGL一般默认开启

3 因为光栅化程序已实现抗锯齿,所以我们设置完子采样点后,看戏就行了。

四 真·用法

要设置自己的多采样点缓冲区,需要两个方法:纹理附加项和渲染缓冲附加项,就像一般的附加项一样。
1 作为多采样点的纹理附加项 Multisampled texture attachments

--创建支持多采样点的纹理附加项,需要把原来的glTexImage2D换成GL_TEXTURE_2D_MULTISAPLE
    

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
    glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);// samples是我们定义的采样点的个数,最后参数置为GL_TRUE,表示每个像素的采样点位置是完全一样的。
     glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);

--使用glFramebufferTexture2D()将多采阳点纹理附加到帧缓冲区。

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

2 作为渲染缓冲对象的多采样点附加项

--如同1步骤,只需要在最后把附加函数换成glRenderbufferStorageMultisample()
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);  

unsigned int rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
    glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT);
    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

 **多采样点缓冲使用步骤说明:
多采样点缓冲比较特别(比如无法在着色器中对其取样),它比正常的图像包含采样点等更多内容,所以我们需要处理图像,进行后处理,这要用到glBlitFramebuffer()函数,该函数可以将多采样点缓冲从一个源帧缓冲区拷贝到目的帧缓冲(GL_READ_FRAMEBUFFER GL_DRAW_FRAMEBUFFER)。 在这个过程中,光栅化程序会处理所有的多采样点信息,也就是说,拷贝到目的帧缓冲的像素,已经进行了抗锯齿处理。而我们在配置这个目的帧缓冲时,绑定一个颜色附加项纹理,将其作为保存抗锯齿处理后的像素。拷贝过后,这个目的帧缓冲里面,就是进行抗锯齿后的纹理,接着,在渲染时,绑定这个纹理,进行正常纹理渲染就好了。

所以,你需要做的事情就是,

1 创建一个framebuffer作为正常渲染对象的帧缓冲,这个帧缓冲包含了多采样点,包含颜色附加项,包含深度和模板缓冲,目的是为了将3D场景渲染到里面。

2 创建第二个framebuffer,这个帧缓冲区附加颜色附加项(以纹理),将其作为屏幕空间,所以不需要深度模板那些,因为屏幕坐标是光栅化的,光栅化程序已处理了多采样点缓冲,所以我们只要像素值来输出。这样,就相当于,将其作为一个纹理输出到屏幕,这个纹理就是从第一个framebuffer拷贝过来的颜色附加项

**拓展:帧缓冲区framebuffer

在以往的渲染中,OpenGL在渲染循环中,先进行默认buffer绑定,所有的渲染操作和设置,包括深度和模板值的数据,都存储在那个默认的buffers里,最后再由glfwSwapBuffers函数,把默认buffers的所有计算和设置,包括深度、模板、颜色的值,
    全部取出来,显示到屏幕上。而不用再渲染循环中绑定buffers(因为glfwSwapBuffers默认的是把buffers里的数据swap出来)

    而Framebuffers,相当于在渲染之前,把颜色缓冲、深度和模板缓冲全部设置好(这个过程叫做配置Framebuffers),如果设置的不对,
    就叫做不完整,可以用函数glCheckFramebufferStatus检查。在渲染循环中,首先要绑定自定义的Framebuffers,所有的渲染操作及其数据,全部会进入这个Framebuffers,然后再用这个framebuffers作为默认buffers的纹理,这样,屏幕显示的就是framebuffers里渲染好的color attachment的内容,再进行glfwSwapBuffers。