3

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

有什么建议么?

4

1 回答 1

11

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

在这种情况下,有几个挑战使混合的使用比平时更加​​困难。首先,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);

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

两阶段 Blitting

您可以使用混合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)。

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

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

于 2015-10-02T04:59:30.907 回答