具有多个颜色附件的多采样帧缓冲区的分辨率

时间:2019-06-19 05:28:16

标签: opengl framebuffer multisampling deferred-rendering deferred-shading

尝试在延期阴影之上实现消除锯齿功能,我尝试使用多重采样的渲染缓冲区,然后使用缓冲区盲区传递来解析采样。

  1. 与延迟着色中的传统方法一样,我正在使用专用着色器渲染场景,该着色器发出3种颜色输出:

    • 位置
    • 普通人
    • 漫反射和镜面反射
  2. 然后将其用于照明计算过程中,以产生最终的场景纹理

  3. 使用简单的着色器将场景纹理渲染到全屏四边形的屏幕上

您可能已经猜到了,屏幕上的MSAA在渲染到屏幕时不会应用于场景纹理的内容: 为了实现抗锯齿,我选择在步骤1)中使用多采样渲染缓冲区,并引入了额外的步骤1.1)进行分辨率。当然,多重采样仅对于彩色图是必要的/有用的,而不是其他两个图。

我的问题是,显然,只能为相同类型的附件定义具有多个渲染缓冲区/颜色附件的帧缓冲区;意味着如果一个附件被多次采样,那么其他所有附件都必须被采样。

在解析过程中,这会成为位置和法线缓冲区的问题,因为抗锯齿会影响几何图形和光照。

  • 我对帧缓冲区附件的理解是否有效?
  • 为了在漫反射和高光贴图上进行多次采样,但又不影响其他贴图,是否有办法解决?
    // Create the frame buffer for deferred shading: 3 color attachments and a depth buffer
    glGenFramebuffers(1, &gBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    {
        // - Position color buffer
        glGenRenderbuffers(1, &gPosition);
        glBindRenderbuffer(GL_RENDERBUFFER, gPosition);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA16F, w, h);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gPosition);

        // - Normal color buffer
        glGenRenderbuffers(1, &gNormal);
        glBindRenderbuffer(GL_RENDERBUFFER, gNormal);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA16F, w, h);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, gNormal);

        // - Color + specular color buffer
        glGenRenderbuffers(1, &gColorSpec);
        glBindRenderbuffer(GL_RENDERBUFFER, gColorSpec);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA, w, h);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, gColorSpec);

        unsigned int attachments[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
        glDrawBuffers(3, attachments);

        // - Generate the depth buffer for rendering
        glGenRenderbuffers(1, &sceneDepth);
        glBindRenderbuffer(GL_RENDERBUFFER, sceneDepth);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_DEPTH_COMPONENT, w, h);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sceneDepth);
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    // Create a frame buffer with 3 attachments for sample resolution
    glGenFramebuffers(1, &gFrameRes);
    glBindFramebuffer(GL_FRAMEBUFFER, gFrameRes);
    {
        glGenTextures(1, &gPositionRes);
        glBindTexture(GL_TEXTURE_2D, gPositionRes);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPositionRes, 0);

        glGenTextures(1, &gNormalRes);
        glBindTexture(GL_TEXTURE_2D, gNormalRes);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_FLOAT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormalRes, 0);

        glGenTextures(1, &gColorSpecRes);
        glBindTexture(GL_TEXTURE_2D, gColorSpecRes);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gColorSpecRes, 0);
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    // ...
    //
    // Once the scene is rendered, resolve:
    glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gFrameRes);
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    glDrawBuffer(GL_COLOR_ATTACHMENT0);
    glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
    glReadBuffer(GL_COLOR_ATTACHMENT1);
    glDrawBuffer(GL_COLOR_ATTACHMENT1);
    glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
    glReadBuffer(GL_COLOR_ATTACHMENT2);
    glDrawBuffer(GL_COLOR_ATTACHMENT2);
    glBlitFramebuffer(0, 0, sw, sh, 0, 0, sw, sh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

上面的代码示例的结果是,被照物体的边缘显示出不适当的暗/黑或亮/白像素的伪像,大概是因为在此过程中它们的位置和/或法线已被更改。

1 个答案:

答案 0 :(得分:2)

  

在解析过程中,这会成为位置和法线缓冲区的问题,因为抗锯齿会影响几何图形和光照。

应该是。

从逻辑上说,位置和法线不使用多重采样是不连贯的,而所获得的漫反射/镜面反射颜色是这样。记住什么是多重采样:每个像素都有多个采样,并且来自重叠三角形的不同数据可能会写入同一像素中的不同采样。因此,您可以在同一像素中具有来自两个或更多三角形的漫反射/镜面反射颜色。但这也意味着您还应该具有与每种子像素颜色关联的位置和法线。否则,您的照明通行证将毫无意义;您将使用位置和正常值来生成不会生成颜色的颜色。

带有延迟渲染的正确多次采样昂贵。使其工作的唯一方法是对所有内容进行多重采样,然后在每个样本级别上执行光照通过计算。由于与超级采样相比,多次采样的大多数性能提升不是按每个样本进行计算,因此,在几何过程中,您只能获得多次采样(而不是超级采样)的好处,而不是光照阶段。

这就是为什么人们在使用延迟渲染时尝试避免多重采样的原因。这就是为什么伪反走样技术(例如FXAA)以及任何现有技术的原因。