在 GLSL 片段着色器中使用整数 - 遇到无法解释的伪像

Using integers in GLSL fragment shaders - experiencing unexplained artifacts

我有一个六边形网格,我想对其进行纹理处理。我想使用一个纹理,其中有 16 个不同的子纹理排列在 4x4 网格中。网格中的每个“节点”都有一个图像类型,我想在它们之间平滑地融合。我实现它的方法是成对渲染三角形,并在两个面的所有顶点上编码 4 种图像类型,以及一组 4 个加权因子(它们是两个三角形的重心坐标)。然后我可以使用这两个东西在任何图像类型组合之间平滑地混合。

这是我正在使用的片段着色器。这些问题是由于使用 int 类型引起的,但我不明白为什么。如果我只使用前四个子纹理,那么我可以将 idx 更改为 float 并将 Y 坐标硬编码为 0,然后它会按我预期的那样工作。

vec2 offset(int idx) {
    vec2 v = vec2(idx % 4, idx / 4);
    return v / 4.0;
}

void main(void) {   
    //
    // divide the incoming UVs into one of 16 regions.  The 
    // offset() function should take an integer from 0..15 
    // and return the offset to that region in the 4x4 map
    //
    vec2 uv = v_uv / 4.0;

    //
    // The four texture regions involved at 
    // this vertex are encoded in vec4 t_txt.  The same
    // values are stored at all vertices, so this doesn't 
    // vary across the triangle
    //
    int ia = int(v_txt.x);
    int ib = int(v_txt.y);
    int ic = int(v_txt.z);
    int id = int(v_txt.w);
    
    //
    // Use those indices in the offset function to get the
    // texture sample at that point
    //      
    vec4 ca = texture2D(txt, uv + offset(ia)); 
    vec4 cb = texture2D(txt, uv + offset(ib)); 
    vec4 cc = texture2D(txt, uv + offset(ic)); 
    vec4 cd = texture2D(txt, uv + offset(id)); 
    
    //
    // Merge them with the four factors stored in vec4 v_tfact.
    // These vary for each vertex
    //
    fragcolour = ca * v_tfact.x 
               + cb * v_tfact.y 
               + cc * v_tfact.z 
               + cd * v_tfact.w;
    
}

事情是这样的:

(我的“一对三角形”实际上是20个左右,你可以在神器中看到它们的结构,但效果是一样的)

这种伪影的行为有点像 z-fighting:四处移动场景使它闪闪发光并疯狂移动。

为什么这不像我预期的那样工作?

我可以退而求其次的一个解决方案是简单地使用一维纹理贴图,所有 16 个子图像都在一条水平线上,然后我可以将所有内容切换为浮点,因为我不需要 modulo/integer-divide 映射 idx->x,y 的过程,但这感觉很笨拙,我至少想了解这里发生了什么。

它应该是这样的,尽管只使用了 4 个子图像:

OpenGL Shading Language 4.60 Specification - 5.4.1. Conversion and Scalar Constructors

When constructors are used to convert a floating-point type to an integer type, the fractional part of the floating-point value is dropped.

因此 int(v_txt.x) 不舍入 v_txt.x,而是截断 v_txt.x

在构造整数值之前,您必须 round 将值设为最接近的整数:

int ia = int(round(v_txt.x));
int ib = int(round(v_txt.y));
int ic = int(round(v_txt.z));
int id = int(round(v_txt.w));

或者在构造积分值之前加0.5:

int ia = int(v_txt.x + 0.5);
int ib = int(v_txt.y + 0.5);
int ic = int(v_txt.z + 0.5);
int id = int(v_txt.w + 0.5);