【问题标题】:Texture lookup into rendered FBO is off by half a pixel对渲染 FBO 的纹理查找减少了半个像素
【发布时间】:2012-07-09 06:25:05
【问题描述】:

我有一个通过 FBO 渲染到纹理的场景,我从片段着色器中对其进行采样,使用图元而不是绘制全屏四边形来绘制它的区域:我通过仅生成片段来节省资源会需要。

为了测试这一点,我发布了与纹理渲染完全相同的几何图形,这意味着生成的光栅化图案应该完全相同:当我的片段着色器使用给定的不同坐标查找其纹理时,它应该与给出的其他值完美匹配。

以下是我如何为片段着色器提供坐标以使用我的全屏纹理自动纹理化几何体:

// Vertex shader
uniform mat4 proj_modelview_mat;
out vec2 f_sceneCoord;
void main(void) {
    gl_Position = proj_modelview_mat * vec4(in_pos,0.0,1.0);
    f_sceneCoord = (gl_Position.xy + vec2(1,1)) * 0.5;
}

我在 2D 中工作,所以我不关心这里的视角鸿沟。我只是使用从 [-1,1] 缩小到 [0,1] 的剪辑空间位置设置了 sceneCoord 值。

uniform sampler2D scene;
in vec2 f_sceneCoord;
//in vec4 gl_FragCoord;
in float f_alpha;   
out vec4 out_fragColor;
void main (void) {
    //vec4 color = texelFetch(scene,ivec2(gl_FragCoord.xy - vec2(0.5,0.5)),0);
    vec4 color = texture(scene,f_sceneCoord);
    if (color.a == f_alpha) {
        out_fragColor = vec4(color.rgb,1);
    } else
        out_fragColor = vec4(1,0,0,1);
}

请注意,如果我的 alpha 不匹配,我会吐出一个红色片段。纹理渲染将每个渲染对象的 alpha 设置为特定索引,因此我知道什么与什么匹配。

抱歉,我没有要显示的图片,但很明显,我的像素偏离了 (0.5,0.5):我的对象周围出现了一个细的、一个像素的红色边框,在它们的底部和左侧,它会弹出出来。它看起来很“短暂”。赠品是它只显示在对象的底部和左侧。

请注意,我有一行注释掉了使用texelFetch:此方法有效,我不再显示我的红色片段。但是,我希望使用texture 和标准化纹理坐标来使其正常工作,因为我认为更多的硬件会支持它。也许真正的问题是,是否有可能在不通过制服发送我的视口分辨率的情况下做到这一点?必须有办法避免这种情况!

更新:我尝试将纹理访问移动半个像素、四分之一像素、百分之一像素,这一切都让情况变得更糟,并在边缘周围产生了一个错误值的实心边框:看起来像我的@ 987654327@ 技巧设置了正确的值,但采样只是稍微偏离了一点点。这很奇怪……看到红色的碎片了吗?当物体在运动时,它们会微微地进进出出。这意味着我设置的 alpha 值与这些像素不完全匹配。

对于我的实际应用程序来说,为 alpha-index-check 获得像素完美的准确性并不重要,但这种行为并不是我所期望的。

【问题讨论】:

    标签: opengl glsl textures shader


    【解决方案1】:

    好吧,首先考虑放弃 f_sceneCoord 变化并仅使​​用 gl_FragCoord / screenSize 作为纹理坐标(您的示例中已经有这个,但 -0.5 是垃圾),screenSize 是统一的(可能是预先-分为)。这应该几乎可以正常工作,因为默认情况下gl_FragCoord 位于像素中心(意思是i+0.5),并且当在纹素中心((i+0.5)/textureSize)对纹理进行采样时,OpenGL 会返回准确的纹素值。

    由于有限的精度等,这可能仍然会与精确的纹素值(如果有)产生非常非常非常轻微的偏差。但话又说回来,无论如何,您可能希望使用GL_NEAREST 的过滤模式来进行这种一对一的纹理到屏幕映射。实际上,您现有的f_sceneCoord 方法可能已经很好地工作了,只是GL_NEAREST 阻止的那些小的舍入问题会创建您的人工制品。但是话又说回来,你仍然不需要f_sceneCoord 的东西。

    编辑:关于texelFetch 的可移植性。我认为该功能是在 GLSL 1.30(~SM4/GL3/DX10-hardware,~GeForce 8)中引入的。但是您正在使用的新 in/out 语法已经需要此版本(与旧的 varying/attribute 语法相反)。因此,如果您不打算更改这些,假设给定的texelFetch 绝对没有问题,并且也可能比texture 稍快(与旧的texture2D 相比,它还需要GLSL 1.30),通过绕过过滤完全。

    【讨论】:

    • 感谢您提出 GL_NEAREST。这很可能会解决问题,因为我可以看到即使基于精度略有不同的线性过滤也会导致我的检查失败。我会尝试我得到的第一次机会。我确实发现我的f_sceneCoord 解决方案比使用FragCoord 和统一的屏幕尺寸更优雅:它的工作原理与我必须跟踪统一的屏幕尺寸无关......复杂性越低总是好的。
    • 我希望我能多次投票!有一个赞成和接受。
    • 是的,我尝试使用 glslDevil 进行调试,它要求我切换回 1.2 语法,所以我将其全部改回 varying/attributetexture2D... glslDevil 仍然没有显示任何值。不管怎样,这没关系,好​​的是GL_NEAREST 产生了完美的结果。结果在我的实际实现中,我仍然需要使用GL_LINEAR,所以事情看起来很顺利,但我真的很高兴已经深入了解了这一点。
    【解决方案2】:

    如果您使用完美的 X,Y [0,1] 且没有舍入误差,那就太好了...但有时 - 特别是在使用极坐标时,您可能会考虑将计算出的坐标与纹理“网格”对齐。 ..

    我用:

    // align it to the nearest centered texel
    curPt -= mod(curPt, (0.5 / vec2(imgW, imgH)));
    

    就像一个魅力,我不再在屏幕边缘出现随机舍入错误......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多