用户的帧缓冲区缺少什么?

时间:2013-08-09 18:07:44

标签: c++ opengl

这是使用投影到屏幕上的帧缓冲纹理和“主帧缓冲区”的相同对象的比较 enter image description here

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

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

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

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);
        }
}

1 个答案:

答案 0 :(得分:3)

FBO在屏幕上的“投影”取决于采样器状态,特别是纹理滤镜状态应该归咎于此。

默认情况下,如果您只是将从FBO绘制的纹理附件绑定到纹理单元并应用它,它将使用 LINEAR 采样。这与直接在屏幕上进行blitting不同,如果你没有使用FBO,传统情况就是如此。

OpenGL中采样器的默认状态表: http://www.opengl.org/registry/doc/glspec44.core.pdf pp.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)如果用一个单位矩阵替换传统的模型视图/投影矩阵(或者只是不将顶点位置乘以顶点着色器中的任何矩阵),则视口的4个角可以是(1,-1,-1)。 / p>

您还应该使用 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直接传递给纹理查找。