opengl - 使用glBlitFramebuffer进行多重采样

标签 opengl antialiasing fbo multisampling

这是我第一次尝试使用opengl进行多重采样(用于抗锯齿)。基本上,我正在为屏幕绘制背景(不应获得抗锯齿),随后我将绘制应进行抗锯齿的顶点。
到目前为止,我所做的是:

//create the framebuffer:
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

//Generate color buffer:
glGenRenderbuffers(1, &cb);
glBindRenderbuffer(GL_RENDERBUFFER, cb);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, x_size, y_size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cb);

//Generate depth buffer:
glGenRenderbuffers(1, &db);
glBindRenderbuffer(GL_RENDERBUFFER, db);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, x_size, y_size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, db);

...

glBindFramebuffer(GL_FRAMEBUFFER, 0);
//draw background ... ...

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//draw things that should get anti-aliased ... ...

//finally:
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, x_size, y_size, 0, 0, x_size, y_size, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);

问题是:当我调用glBlitFramebuffer(...)时,整个背景变黑,并且我只看到抗锯齿的顶点。

有什么建议么?

最佳答案

通常,如果要在现有渲染的基础上渲染新图像/纹理同时考虑图像的透明度,则混合是最明显的选择。将呈现给多采样帧缓冲区的渲染看成是具有透明性的图像,这正是您遇到的情况。

在这种情况下,存在许多挑战,使混合的使用比平常更加困难。首先,glBlitFramebuffer()不应用混合。从规格:

Blit operations bypass the fragment pipeline. The only fragment operations which affect a blit are the pixel ownership test and the scissor test.



如果没有多重采样,这是很容易克服的。无需使用glBlitFramebuffer(),而是通过绘制屏幕大小的带纹理的四边形来执行blit。由于所有片段操作都在进行中,因此可以使用混合。

但是,由于您的内容是多采样的,因此“绘制带纹理的四边形”部分会变得更加棘手。我想到了一些选择。

将背景渲染到FBO

您可以将背景渲染到多次采样的FBO而不是主帧缓冲区。然后,您可以像现在一样完全使用glBlitFramebuffer()

您可能会想:“但是我不希望我的背景受到锯齿!”那不是真正的问题。您只需在绘制背景时禁用多重采样:
glDisable(GL_MULTISAMPLE);

我认为那应该给您您想要的。如果可以的话,这是迄今为止最简单的选择。

多样本纹理

OpenGL 3.2和更高版本支持多样本纹理。为此,您将使用纹理而不是渲染缓冲区作为FBO的颜色缓冲区。纹理分配有:
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8,
                         xsize, ysize, GL_FALSE);

还有其他方面,我不能全部在这里讨论。如果您想探索此选项,则可以阅读规范或其他来源中的所有详细信息。例如,着色器代码中纹理的采样以不同的采样器类型以不同的方式工作,并且采样功能一次仅允许您读取一个采样。

两阶段发短信

您可以混合使用glBlitFramebuffer()来解析多样本内容,并使用“manual” blit来将内容混合到默认帧缓冲区中:
  • 创建第二个FBO,其中颜色附件是常规的而不是多重采样的纹理。
  • 使用glBlitFramebuffer()从第一个FBO中的多重采样渲染缓冲区复制到第二个FBO中的纹理。
  • 设置并启用混合。
  • 使用作为第二个FBO附件的纹理绘制屏幕大小的四边形。

  • 尽管这看起来有些尴尬,并且需要一个额外的副本,这对于性能来说是不希望的,但它相当简单。

    最后渲染背景

    为此,您可以按照现在的操作进行操作,使用glBlitFramebuffer()将多次采样的FBO内容复制到默认的帧缓冲区中。但是您首先执行此操作,然后渲染背景,然后渲染

    您可能会认为这是行不通的,因为它会将背景放在其他内容的前面,这使得它……没有太多背景。

    但是在这里融合再次发挥作用。虽然将内容混合在其他内容之上是使用混合的最常见方法,但是您也可以使用它在现有内容后面渲染事物。为此,您需要做一些事情:
  • 具有alpha平面的帧缓冲区。请求方式取决于您用于OpenGL设置的窗口系统/工具包。通常位于请求深度缓冲区,模板缓冲区(如果需要)等的同一区域。通常将其指定为多个alpha平面,通常将其设置为8。
  • 正确的混合功能。对于前后混合,通常使用:
    glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
    

    这会在以前未渲染任何内容的情况下添加新的渲染(即目标中的alpha为0),而在已经渲染过的地方(即目标alpha为1)将保持先前的渲染不变。

    如果渲染涉及部分透明性,则混合设置可能会有些棘手。

  • 这可能看起来有些复杂,但是当您绕过混合功能的工作原理时,这确实非常直观。而且,我认为这总体而言是解决您整体问题的一种优雅而有效的解决方案。

    关于opengl - 使用glBlitFramebuffer进行多重采样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32884394/

    相关文章:

    antialiasing - 如何关闭 Direct3D 输出过滤

    opengl - 使用像素缓冲区对象 (PBO) 从帧缓冲区对象 (FBO) 读取像素值

    java - LWJGL - OpenGL 上下文在关闭 Hook 中丢失

    opengl - glsl 片段着色器 - 如何将 GL_NEAREST 纹理采样为 GL_LINEAR

    ios - 来自 Swift 代码的抗锯齿像素被 OpenCV 转换为不需要的黑色像素

    opengl - 通过 FBO 使用“渲染到目标”时是否需要重新生成 MipMap?

    macos - 如何在共享上下文中渲染到 FBO?

    c++ - 用 GLUT 绘制大文本?

    c++ - 创建一个 3d 立方体和弹跳球

    android - Paint.setAntiAlias(false) 没有效果?