以下任何纹理查询会导致未定义的行为,不均匀的流动或同时导致两者吗?

时间:2018-12-12 01:05:48

标签: opengl glsl opengl-3

我正在使用顶点着色器,片段着色器和少量纹理图集(包含数十个较小的精灵的图像)将精灵绘制到屏幕上。我的目标是对整个场景使用一次绘制调用,因此需要创建一个可以基于属性动态选择纹理的着色器。依次绑定每个纹理图集,并发送平面属性textureid以确定要使用的纹理,然后由uv发送该纹理的区域。

GLSL 3.30规范指出,采样器数组需要一个常量表达式作为索引,但是以下编译和链接没有错误(在最新的Nvidia驱动程序上):

#version 330

uniform sampler2D sampler[4];
in vec2 uv;
flat in int textureid;
out vec4 endcolor;

void main() {
    endcolor = texture(sampler[textureid], uv);
}

我不能保证在所有硬件上都可以使用它,并且对于为什么没有任何警告就链接它感到困惑。然后,我决定尝试以下方法:

void main() {
    if (textureid == 0) {
        endcolor = texture(sampler[0], uv);
    } else if (textureid == 1) {
        endcolor = texture(sampler[1], uv)
    } else if (textureid == 2) {
        endcolor = texture(sampler[2], uv);
    } else if (textureid == 3) {
        endcolor = texture(sampler[3], uv);
    } else {
        endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

我了解到,这将导致不确定的行为,因为它依赖于非均匀流控制。它采样的纹理取决于输入属性。因此,我然后将其更新为:

void main() {
    vec4 one = texture(sampler[0], uv);
    vec4 two = texture(sampler[1], uv);
    vec4 three = texture(sampler[2], uv);
    vec4 four = texture(sampler[3], uv);

    if (textureid == 0) {
        endcolor = one;
    } else if (textureid == 1) {
        endcolor = two;
    } else if (textureid == 2) {
        endcolor = three;
    } else if (textureid == 3) {
        endcolor = four;
    } else {
        endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

这三种方法中的一种:

  1. 哪些会导致未定义的或错误的行为,而不会,为什么?
  2. 是否还有其他更好的方法(sampler2DArray除外)?
  3. 为什么第一种方法可以继续编译,链接和正常工作?

我知道我可以使用sampler2DArray,但是我的图片可能尺寸不同。

1 个答案:

答案 0 :(得分:4)

void main() {
  endcolor = texture(sampler[textureid], uv);
}

这在GLSL 3.30时期不起作用,因为3.30不允许您通过非常量表达式索引不透明类型的数组。 NVIDIA的编译器允许在某些平台上执行此操作无关紧要:规范说您不能这样做。


void main() {
    if (textureid == 0) {
        endcolor = texture(sampler[0], uv);
    } else if (textureid == 1) {
        endcolor = texture(sampler[1], uv)
    } else if (textureid == 2) {
        endcolor = texture(sampler[2], uv);
    } else if (textureid == 3) {
        endcolor = texture(sampler[3], uv);
    } else {
        endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

这也是错误的,但是出于(略有)不同的原因。您访问non-uniform control flow, which makes implicit derivatives undefined中的内容。解决此问题的方法是在访问纹理之前获取导数,然后使用textureGrad将它们传递给:

void main() {
    vec2 uvDx = dFdx(uv);
    vec2 uvDy = dFdy(uv);
    switch(textureid) {
    case 0:
        endcolor = textureGrad(sampler[0], uv, uvDx, uvDy);
        break;
    case 1:
        endcolor = textureGrad(sampler[1], uv, uvDx, uvDy);
        break;
    case 2:
        endcolor = textureGrad(sampler[2], uv, uvDx, uvDy);
        break;
    case 3:
        endcolor = textureGrad(sampler[3], uv, uvDx, uvDy);
        break;
    default:
       endcolor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

  

为什么第一种方法可以继续编译,链接和正常工作?

因为NVIDIA将会成为NVIDIA。他们真的不在乎确保您不会意外使用您不应该使用的功能,也不会遵循规范的明确措辞。