4

这是使用投影到屏幕上的帧缓冲区纹理和“主帧缓冲区”的相同对象的比较 在此处输入图像描述

左图像有点模糊,而右图像更清晰。此外,某些选项(例如glPolygonMode( GL_FRONT_AND_BACK, GL_LINE )在渲染到帧缓冲区时无法正常工作)。我的“管道”看起来像这样

Bind frambuffer
draw all geometry
Unbind
Draw on Quad like as texture.

所以我想知道为什么“主帧缓冲区”可以做到这一点而“我的”不能?这两者有什么区别?用户帧缓冲区是否会跳过某些阶段?是否可以匹配主缓冲区的质量?

void Fbo::Build()
{
        glGenFramebuffers(1, &fboId);
        glBindFramebuffer(GL_FRAMEBUFFER, fboId);

        renderTexId.resize(nColorAttachments);
        glGenTextures(renderTexId.size(),&renderTexId[0]);

        for(int i=0; i<nColorAttachments; i++)
        {
                glBindTexture(format,renderTexId[i]);
                glTexParameterf(format, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                glTexParameterf(format, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexParameteri(format, GL_TEXTURE_WRAP_S, GL_CLAMP);
                glTexParameteri(format, GL_TEXTURE_WRAP_T, GL_CLAMP);
                glTexImage2D(format, 0, type, width, height, 0,  type, GL_FLOAT, 0);
                glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,renderTexId[i], 0);
        }
        glBindTexture(GL_TEXTURE_2D, 0);

        if(hasDepth)
        {
                glGenRenderbuffers(1, &depthBufferId);
                glBindRenderbuffer(GL_RENDERBUFFER, depthBufferId);
                glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);

                //glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24, width, height, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
                glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferId);
        }

        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE)
        {
                printf("FBO error, status: 0x%x\n", status);
        }
}
4

1 回答 1

3

FBO 在屏幕上的“投影”受采样器状态的影响,特别是纹理过滤器状态是这里的罪魁祸首。

默认情况下,如果您只是将您从 FBO 中提取的纹理附件绑定到纹理单元并应用它,它将使用线性采样。这与不使用 FBO 的情况下直接将 blitting 直接传输到屏幕不同。

OpenGL 中采样器的默认状态表: http ://www.opengl.org/registry/doc/glspec44.core.pdf第 541 页,表 23.18 纹理(每个采样器对象的状态)

如果您想在没有 FBO 的情况下复制绘图效果,您需要在视口上拉伸一个四边形(或两个三角形),并为纹理过滤器使用NEAREST邻居采样。否则,它将在您的 FBO 中对相邻的纹素进行采样,并为屏幕上的每个像素插入它们。这是左侧图像更平滑的原因,它说明了一种抗锯齿形式。值得一提的是,这甚至与 MSAA 或 SSAA 不同,后者会在几何被光栅化以修复欠采样错误时提高采样率,但它确实达到了类似的效果。

然而,有时这是可取的。许多处理密集型算法以 1/4、1/8 或更低的分辨率运行,然后使用双线性或双边滤波器上采样到视口分辨率,而不会出现与最近邻采样相关的块状问题。


多边形模式状态应该可以正常工作。不过,在将四边形绘制到视口上之前,您需要记住将其设置回 GL_FILL。同样,这一切都回到了这里的状态管理 - 您的四边形将需要一些非常具体的状态来产生一致的结果。为了有效地呈现这种方式,您可能必须实现更复杂的状态管理系统/批处理器,您不能再简单地glPolygonMode (...)全局设置一次并忘记它:)


更新:

感谢 datenwolf 的评论,应该注意的是,上面对纹理过滤的讨论是在假设您的 FBO 的分辨率与您试图拉伸它的视口不同的情况下进行的。

如果您的 FBO 和视口具有相同的分辨率,并且您仍然从LINEAR纹理过滤中获得这些伪影,那么您没有正确设置纹理坐标。这种情况下的问题是,您在除纹素中心以外的位置对 FBO 纹理进行采样,这会导致不需要插值的地方。

默认情况下,在 GLSL 中,片段在其中心(非多重采样)进行采样,因此,如果您正确设置了顶点纹理坐标和位置,则无需对每个顶点纹理坐标进行任何纹理偏移数学运算。但是,如果您尝试进行 1:1 映射,透视投影可能会毁了您的一天,因此您应该使用正交投影,或者在将四边形绘制在视口上时,最好使用NDC 坐标并且根本不使用投影。

您可以在标准化设备坐标中使用以下顶点坐标:(-1,-1,-1), (-1,1,-1), (1,1,-1),(1,-1,-1 ) 对于视口的 4 个角,如果您用单位矩阵替换传统的模型视图/投影矩阵(或者根本不将顶点位置乘以顶点着色器中的任何矩阵)。

您还应该使用CLAMP_TO_EDGE作为您的环绕状态,因为这将确保您永远不会在给定方向 (s,t) 上生成第一个纹素的中心和最后一个纹素的中心范围之外的纹理坐标。CLAMP实际上会为位于或超出 FBO 纹理附件边缘的任何内容生成值 0 和 1(它们不是纹素中心)。

作为奖励,如果您总是打算以 1:1 渲染(FBO 与视口),则可以完全避免使用逐顶点纹理坐标并使用gl_FragCoord. 默认情况下,在 GLSL 中,gl_FragCoord将为您提供片段中心的坐标 (0.5, 0.5),这也恰好是您的 FBO 中相应的纹素中心。gl_FragCoord.st在这种特殊情况下,您可以直接传递给纹理查找。

于 2013-08-09T18:58:12.370 回答