代码之家  ›  专栏  ›  技术社区  ›  tuket

OpenGL奇异线线性滤波

  •  1
  • tuket  · 技术社区  · 7 年前

    我编写了以下着色器,用于测试OpenGL中线性过滤的工作方式。

    enter image description here

    在这里,我们有一个5x1纹理飞溅到一个立方体的表面上(megenta区域只是背景的颜色)。

    纹理就是这个(非常小)。

    enter image description here

    这个 波顿左 角对应于 uv=(0, 0) 右上角 对应于 uv=(1, 1) . 线性滤波已启用。

    着色器垂直分割 v 5行坐标(从上到下):

    1. 连续采样。正常取样即可。
    2. 如果u在[0,1]中,则为绿色,否则为红色。仅用于测试目的。
    3. 灰度中的u坐标。
    4. 在纹理左侧采样。
    5. 在纹理中心采样。

    问题是 3 4 有一行像素闪烁。闪烁会随着相机距离的改变而改变,有时甚至可以使其消失。问题似乎出在处理第四行的着色器代码中。

    // sample at the left of the pixel
    // the following line can fix the problem if I add any number different from 0
    tc.y += 0.000000;  // replace by any number other than 0 and works fine
    tc.x = floor(5 * tc.x) * 0.2;
    c = texture(tex0, tc);
    

    我觉得这很奇怪,因为在那个区域 v 坐标不靠近纹理的任何边缘。

    1 回复  |  直到 7 年前
        1
  •  4
  •   derhass    7 年前

    您的代码依赖于 未定义的值 在纹理提取期间。

    这个 GLSL 4.60 specification 第8.9节纹理函数(emphasis mine)中的状态:

    某些纹理函数(非Lod和非梯度版本)可能需要 隐式导数。 隐式导数在 非均匀控制流 对于非片段着色器纹理获取。

    虽然大多数人认为这些导数仅用于mip映射,但这是不正确的。还需要LOD因子来确定纹理是放大还是缩小(以及在非mipmapped情况下的各向异性过滤,但这在这里并不重要)。

    图形处理单元通常通过2x2像素四元组中相邻像素之间的有限差分来近似导数。 发生的事情是,在各种选项之间的边缘,你有非均匀的控制流,其中一行你做纹理过滤,而在上面一行,你不做。有限差分将导致尝试访问上行中纹理采样操作的纹理坐标,这根本不能保证已计算,因为着色器调用没有主动执行该代码路径-这就是规范将其视为未定义的原因。

    现在,根据边缘在2x2像素四边形中的位置,可以得到正确的结果,也可以不得到。对于无法得到正确结果的情况,一个可能的结果可能是GL使用缩小滤波器,这是 GL_NEAREST 在你的例子中。

    只需将两个过滤器设置为 GL_LINEAR . 然而,这仍然不是正确的代码,因为根据规范,结果仍然没有定义。

    唯一正确的解决方案是将纹理采样移出非均匀控制流,如

    vec4 c1=texture(tex, tc); // sample directly at tc
    vec4 c2=texture(tex, some_function_of(tc)); // sample somewhere else
    vec4 c3=texture(tex, ...);
    
    // select output color in some non-uniform way
    if (foo) {
       c=c1;
    } else if (bar) {
       c=c2;
    } else {
       c=c3;
    }