什么用户的帧缓冲区缺乏? [英] What user's framebuffer lacks?

查看:203
本文介绍了什么用户的帧缓冲区缺乏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



左图像有点模糊,右边更清晰。有一些选项像 glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)在渲染到帧缓冲区时不能正常工作。
我的管道看起来像这样

 绑定frambuffer 
绘制所有几何图形
Unbind
在纹理上绘制四边形。所以我想知道为什么主要frambufffer可以做到这一点,而我的不能?这两者之间有什么区别?用户帧缓冲是否跳过一些阶段?是否可以匹配主缓冲区的质量?

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

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

for(int i = 0; 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\\\
,status);
}
}


解决方案



默认情况下,如果你简单地绑定纹理,那么FBO在屏幕上的投影会受到采样器状态的影响,特别是纹理过滤器的状态。附件,你从FBO到纹理单元并应用它,它将使用线性抽样。



OpenGL中的Samplers的默认状态表:

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



如果要复制没有FBO,您需要在视口上拉伸四边形(或两个三角形),并对纹理过滤器使用 NEAREST 邻居采样。否则,它将对FBO中的相邻纹素进行抽样,并对屏幕上的每个像素进行插值。这是你左侧更平滑的图像的原因,这说明了一种反锯齿的形式。值得一提的是,这甚至不像MSAA或SSAA那样接近相同的东西,当光栅化几何图形以固定欠采样误差时,这增加了采样率,但它的确获得了类似的效果。



然而,有时候这是可取的。许多处理密集型算法以1/4,1/8或更低的分辨率运行,然后使用双线性或双边滤波器来上采样到视口分辨率,而没有与最近邻居采样相关联的块效应。




多边形模式状态应该正常工作。你需要记住把它设置回GL_FILL之前你的四边形在视口上绘制。再次,这一切都回到了国家管理这里 - 你的四元组将需要一些非常具体的状态,以产生一致的结果。要有效地渲染这种方式,你可能需要实现一个更复杂的状态管理系统/批处理器,你不能再简单地设置 glPolygonMode(...)一次, it:)






UPDATE:感谢datenwolf的意见,应该注意,上面的纹理过滤的讨论是假设你的FBO是在不同的分辨率,而不是你想要伸展它的视口。



如果您的FBO和视口使用相同的分辨率,但您仍然从 LINEAR 纹理过滤中获取这些工件,那么您尚未设置纹理坐标正确。这种情况下的问题是,您在纹理像素中心以外的位置对FBO纹理进行采样,这是不必要的插值。



片段在默认情况下,它们的中心(非多重采样)在GLSL中,因此如果您正确设置顶点纹理坐标和位置,则不必对每个顶点纹理坐标执行任何纹理偏移运算。透视投影可以毁掉你的一天,如果你想做1:1映射,所以你应该使用正投影,或更好,但使用 NDC坐标

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



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 直接传递给您的纹理查找。


Here is a comparison of same object using framebuffer texture projected onto screen and "main framebuffer"

Left image is bit blured while right is more sharp.Alos some options like glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ) do not work properly while rendering into the framebuffer. My "pipeline" looks like this

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

So I wondering why "main frambufffer" can do this while "mine" can't? What are the differences between those two? Does user framebuffers skips some stages? Is it possible to match the quality of main buffer?

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

解决方案

Your "projection" of the FBO onto the screen is subject to sampler state, in particular the texture filter state is to blame here.

By default, if you simply bind the texture attachment you drew into from your FBO to a texture unit and apply it, it is going to use LINEAR sampling. This is different from blitting directly to the screen as would traditionally be the case if you were not using an FBO.

Default State table for Samplers in OpenGL: http://www.opengl.org/registry/doc/glspec44.core.pdf pp. 541, Table 23.18 Textures (state per sampler object)

If you want to replicate the effect of drawing without an FBO, you would want to stretch a quad (or two triangles) over your viewport and use NEAREST neighbor sampling for your texture filter. Otherwise, it is going to sample adjacent texels in your FBO and interpolate them for each pixel on screen. This is the cause of your smoother image on the left side, which illustrates a form of anti-aliasing. It is worth mentioning that this is not even close to the same thing as MSAA or SSAA, which increase the sample rate when geometry is rasterized to fix undersampling errors, but it does achieve a similar effect.

Sometimes this is desirable, however. Many processing intensive algorithms run at 1/4, 1/8, or lower resolution and then use a bilinear or bilateral filter to upsample to the viewport resolution without the blockiness associated with nearest neighbor sampling.


The polygon mode state should work just fine. You will need to remember to set it back to GL_FILL before you draw your quad over the viewport though. Again, it all comes back to state management here - your quad will require some very specific states to produce consistent results. To render this way effectively you will probably have to implement a more sophisticated state management system / batch processor, you can no longer simply set glPolygonMode (...) once globally and forget it :)


UPDATE:

Thanks to datenwolf's comments, it should be noted that the above discussion of texture filtering was under the assumption your FBO was at a different resolution than the viewport you were trying to stretch it over.

If your FBO and viewport are at the same resolution, and you are still getting these artifacts from LINEAR texture filtering, then you have not setup your texture coordinates correctly. The problem in this scenario is that you are sampling your FBO texture at locations other than the texel centers and this is causing interpolation where none should be necessary.

Fragments are sampled at their centers (non-multisample) in GLSL by default, so if you setup your vertex texture coordinates and positions correctly you will not have to do any texel offset math on your per-vertex texture coordinates. Perspective projection can ruin your day if you are trying to do 1:1 mapping though, so you should either use orthographic projection, or better yet use NDC coordinates and no projection at all when you draw your quad over the viewport.

You can use the following vertex coordinates in Normalized Device Coordinates: (-1,-1,-1), (-1,1,-1), (1,1,-1),(1,-1,-1) for the 4 corners of your viewport if you replace the traditional modelview / projection matrices with an identity matrix (or simply do not multiply the vertex position by any matrix in your vertex shader).

You should also use CLAMP_TO_EDGE as your wrap state, because this will ensure you never generate texture coordinates outside the range of the center of the first texel and the center of the last texel in a given direction (s,t). CLAMP will actually generate values of 0 and 1 (which are not texel centers) for anything at or beyond the edges of the FBO texture attachment.

As a bonus, if you ALWAYS intend to render at 1:1 (FBO vs. viewport), you can avoid using per-vertex texture coordinates altogether and use gl_FragCoord. By default in GLSL, gl_FragCoord will give you the coordinate for the fragment center (0.5, 0.5), which also happens to be the corresponding texel center in your FBO. You can pass gl_FragCoord.st directly to your texture lookup in this special case.

这篇关于什么用户的帧缓冲区缺乏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆