【问题标题】:What user's framebuffer lacks?用户的帧缓冲区缺少什么?
【发布时间】:2013-08-12 08:57:32
【问题描述】:

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

左图有点模糊,而右图更清晰。此外,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);
        }
}

【问题讨论】:

  • 这很有趣,左边的图像对我来说看起来更干净。看起来左边的图像正在做一些抗锯齿。
  • 看起来也是GL_LINEAR(左)和GL_NEAREST(右)的区别
  • 看起来像抗锯齿。
  • 左边更平滑,好像在使用抗锯齿。如果它是全屏四边形上的纹理,GL_LINEAR 过滤不会做同样的事情吗?

标签: c++ opengl


【解决方案1】:

FBO 在屏幕上的“投影”受采样器状态的影响,尤其是纹理过滤器状态。

默认情况下,如果您只是将您从 FBO 中提取的纹理附件绑定到纹理单元并应用它,它将使用 LINEAR 采样。这与不使用 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 的 cmets,应该注意的是,以上关于纹理过滤的讨论是在假设您的 FBO 与您尝试拉伸它的视口处于不同分辨率的情况下进行的。

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

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

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

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

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

【讨论】:

  • 对于任何类型的插值函数,如果你在采样点进行评估,你必须得到准确的样本值,而不是插值。或者用外行的话:它与 LINEAR 与 NEAREST 没有任何关系。该问题与未评估原始采样点(像素)处的纹理有关。也就是说,因为 0 和 1 坐标不在像素中心,而是在像素边界上。
  • 我很确定它与 LINEAR 与 NEAREST 有一切。当您在中心以外的地方对纹素进行采样时会发生什么?如果您没有使用最近邻过滤器,它将获取相邻的纹素并进行插值。当然,更好的解决方案是使用适当的顶点坐标绘制四边形,以便每个片段的 UV 纹理坐标与纹素中心对齐,但这只有在 FBO 的分辨率与视口匹配时才会发生。在 OpenGL/D3D10+ 中,这比 D3D9 更容易实现。
  • 使用 nearest 过滤无法补救,因为它会在纹理和屏幕像素网格之间引入锯齿。因此,根据实际分辨率,可能会省略一些行/列,而另一些则消失。不,唯一真正的解决方案是使用正确的 纹理坐标 或使用 texelFetch 着色器指令对纹理像素中心进行采样。见stackoverflow.com/a/17422950/524368
  • 我们似乎以两种不同的方式思考这种情况。我担心的是,他的图片中屏幕上的片段数量与 FBO 纹理附件的分辨率之间没有完美的 1:1 映射。屏幕左侧的视口甚至不是相同的纵横比。我不确定在这种情况下如何显式地使用texelFetch (...) 获取纹素会产生不同的行为?实际上,每个片段的相对区域可能大于或小于单个纹素,因此您不能让每个片段在没有固有锯齿的情况下对纹素中心进行采样。
  • 好吧,假设视口为 160×120,纹理为 256×256,那么完全可以将纹理的 160×120 子部分像素完美映射到视口; (理想情况下使用 texelFetch 完成)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-19
  • 1970-01-01
相关资源
最近更新 更多