当前位置: 首页 > 知识库问答 >
问题:

以下纹理查找会导致未定义的行为、不均匀的流动,还是两者都会导致?

商焕
2023-03-14

我正在使用顶点着色器、片段着色器和少量纹理图集(包含数十个较小精灵的图像)将精灵绘制到屏幕上。我的目标是为整个场景使用单个绘图调用,因此需要创建一个可以根据属性动态选择纹理的着色器。每个纹理图集都按顺序绑定,并发送一个平面属性texture id以确定要使用的纹理,该纹理的区域由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. 为什么第一个方法继续编译、链接和工作没有错误?

我知道我可以使用数组,但我的图像可能大小不同。

共有1个答案

楚宏胜
2023-03-14
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);
    }
}

这也是错误的,但原因(略有不同)。访问非均匀控制流中的那些,这使得隐式导数未定义。解决这一问题的方法是在访问纹理之前获取导数,然后使用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);
    }
}

为什么第一个方法继续编译、链接和工作而没有错误?

因为英伟达将成为英伟达。他们并不真正关心确保你没有意外地使用你不应该使用的功能,或者遵循规范的明确措辞。

 类似资料:
  • 计算数组长度有一个众所周知的模式: 此模式适用于静态数组和恒定大小的自动数组。它也适用于C99中的可变长度数组。 我想应用类似的想法来计算动态数组的大小(以字节为单位): 这比 更好不是指实际的数组元素类型。因此,它不需要代码的读者知道类型。 然而,我不清楚表达式是否会导致未定义的行为。随着它的扩展: 如果为,则结果表达式有问题: 根据C99标准: (C99,6.3.2.3p3):“具有值 运算符

  • 在寻找将s组合为的方法时,我在几篇文章中看到,使用是推荐的方法: 但是,我想知道为什么的进一步用法可以定义行为。 如果它不是,而是一个复杂的类对象,那么访问它肯定也不会被定义,对吗?那么,为什么会是这种情况呢? Edit:为了使我的问题清楚,我想指定我的目标:我想找到一种方法,将几个组合成一个并进一步使用这个double,而不会导致未定义的行为。我不希望指定的值。无论如何,我认为这是不可能的,因为

  • 所以我对这段代码有了一些了解: 在下面的所有内容中,我假设编译器不能对或的范围有任何先入为主的概念,初始化器仅用于上面的示例。 如果我在一个32位的整数编译器上编译这个(比如在编译x86的时候),没问题。编译器会简单地使用和作为类型值(不能进一步提升它们),乘法会简单地给出注释所说的结果(模在这种情况下是0x100000000)。 然而,如果我在一个64位整数大小的编译器上编译这个(例如x86-6

  • 问题内容: 在Chrome中,然后在Firefox或InternetExplorer中。图像原本为120像素,我正在缩小为28像素,但是无论您将其缩小为多少,它看起来都非常糟糕。 图像是PNG,并且具有Alpha通道(透明度)。 以下是相关代码: HTML: CSS: CSS 的和行似乎没有任何作用,但是我在对该问题进行一些研究时在网上找到了它们。 问题答案: 看来你是对的。没有选项可以更好地缩放

  • 我有一个程序可以将USB数据记录到我创建的文件中,它工作正常。我正在尝试使用setText显示其中的一些数据,但setText正确执行一次,然后下次调用它时,我得到了一个未找到源的崩溃。 在此运行中,我将USB数据保存到我的文件中: 如果我rem掉上面的setText行,程序就会运行得很愉快,将数据保存到一个文件中。当我像上面那样运行代码时,程序崩溃了,我得到了一个未找到源代码的指示。 如果我在s

  • 正常的在均衡比如:增加消费者或者增加分区,没有宕机的事故,会不会导致消息丢失或者重复消费呢? 我记得在均衡时会强制消费者提交偏移量,应该不会出现上述问题把?