从OpenGL Compute Shader中的不同采样器读取结果会导致奇数像素化

问题描述

当使用OpenGL 4.5计算着色器时,按照以下方式从基于变量的不同纹理进行采样会导致奇数像素化。 (第一个纹理是红色,第二个纹理是蓝色)

#version 450

uniform sampler2D textures[2];

layout (binding = 0,rgba32f) uniform image2D framebuffer;

layout (local_size_x = 32,local_size_y = 32) in;

void main() {
    ivec2 pix = ivec2(gl_GlobalInvocationID.xy);
    ivec2 size = imageSize(framebuffer);
    if (pix.x >= size.x || pix.y >= size.y) {
        return;
    }
    vec2 tex_coords = vec2(pix)/size;

    int index;
    vec4 col;
    if (tex_coords.x > tex_coords.y) {
        index = 0;
    } else {
        index = 1;
    }

    /* This works */
    // for (int i=0; i<=index; i++)
    //     if (i==index)
    //         col = textureLod(textures[index],vec2(0,0),0);

    /* These don't */
    col = textureLod(textures[index],0);
    // col = texelFetch(textures[index],ivec2(0,0);

    imageStore(framebuffer,pix,col);
}

enter image description here

奇怪的是,使用while循环抵消不同纹理的样本发生时的偏移量似乎可以解决此问题。使用大小为1的工作组似乎也可以解决该问题。任何人都知道为什么会发生这种行为和/或没有那么简单的方法来阻止它吗?

有关代码的完整MRE,请访问https://github.com/Luminic/TextureSamplingIssues

解决方法

您得到的结果完全符合规范。 GLSL规范指出:

纹理组合的采样器类型是不透明的类型,声明和行为如上所述。 不透明的类型。 在着色器中聚合到数组中时,只能用 动态统一整数表达式,否则结果不确定。

您实际上无法做到这一点,并且循环的解决方法仍然是未定义的行为(尽管按照当前GPU的工作方式更可能起作用)。正确的解决方法是:

vec4 texSamples[2];
texSamples[0]=texture(textures[0],...);
texSamples[1]=texture(textures[1],...);
col = texSamples[index];

但是,您也许可以使用阵列纹理,您可以在其中通过任意非均匀表达式选择图层。