【问题标题】:Multisampling with glBlitFramebuffer使用 glBlitFramebuffer 进行多重采样
【发布时间】:2015-10-01 09:45:05
【问题描述】:

这是我第一次尝试使用 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(...) 时,整个背景变黑,我只看到抗锯齿顶点。

有什么建议吗?

【问题讨论】:

    标签: opengl antialiasing fbo multisampling


    【解决方案1】:

    通常,如果您想在现有渲染之上渲染新图像/纹理,同时考虑图像的透明度,则混合是最明显的选择。将多采样帧缓冲区的渲染视为具有透明度的图像,这正是您所遇到的情况。

    在这种情况下,有几个挑战使混合的使用比平时更加​​困难。首先,glBlitFramebuffer() 不应用混合。来自规范:

    Blit 操作绕过片段管道。唯一影响 blit 的片段操作是像素所有权测试和剪刀测试。

    如果没有多重采样,这很容易克服。不使用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() 来解析多样本内容,并使用“手动” blit 将内容混合到默认帧缓冲区中:

    1. 创建第二个 FBO,其中颜色附件是常规的而非多重采样的纹理。
    2. 使用 glBlitFramebuffer() 从第一个 FBO 中的多重采样渲染缓冲区复制到第二个 FBO 中的纹理。
    3. 设置并启用混合。
    4. 使用作为第二个 FBO 附件的纹理绘制一个屏幕大小的四边形。

    虽然这看起来有些尴尬,并且需要额外的副本,这对性能来说是不受欢迎的,但它相当简单。

    最后渲染背景

    为此,您只需执行您现在正在做的事情,将多重采样的 FBO 内容复制到带有 glBlitFramebuffer() 的默认帧缓冲区。但是您先执行此操作,然后渲染背景

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

    但这就是混合再次发挥作用的地方。虽然在其他内容之上混合内容是使用混合的最常见方式,但您也可以使用它来渲染现有内容背后的内容。为此,您需要做一些事情:

    • 带有 alpha 平面的帧缓冲区。您如何请求取决于您用于 OpenGL 设置的窗口系统/工具包。它通常位于您请求深度缓冲区、模板缓冲区(如果需要)等的同一区域。它通常指定为多个 alpha 平面,通常设置为 8。
    • 正确的混合功能。对于前后混合,您通常使用:

      glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
      

      这会在之前没有渲染的地方添加新的渲染(即目标中的 alpha 为 0),并将在已经渲染的地方保持之前的渲染不变(即目标 alpha 为 1)。

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

    这可能看起来有些复杂,但是一旦您了解了混合函数的工作原理,它就会非常直观。而且我认为它总体上是解决您的整体问题的优雅而有效的解决方案。

    【讨论】:

    • 感谢您的详细解答!
    猜你喜欢
    • 2019-01-14
    • 2014-06-12
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-08
    相关资源
    最近更新 更多