以下任何纹理查找是否会导致未定义的行为、非均匀流或两者兼而有之?
Will any of the following texture lookups cause undefined behavior, non-uniform flow, or both?
我正在使用顶点着色器、片段着色器和少量纹理图集(包含数十个较小精灵的图像)将精灵绘制到屏幕上。我的目标是对整个场景使用单个绘制调用,因此需要创建一个可以根据属性动态选择纹理的着色器。每个纹理图集按顺序绑定并发送平面属性 textureid
以确定要使用的纹理,并且该纹理的区域由 uv
.
发送
GLSL 3.30 规范说采样器数组需要一个常量表达式作为索引,但以下编译和 links 没有错误(在最新的 Nvidia 驱动程序上):
#version 330
uniform sampler2D sampler[4];
in vec2 uv;
flat in int textureid;
out vec4 endcolor;
void main() {
endcolor = texture(sampler[textureid], uv);
}
我不能保证这适用于所有硬件,并且很困惑为什么它 linked 没有任何警告。然后我决定尝试以下方法:
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);
}
}
在这三种方法中:
- 哪些会导致未定义或错误的行为,哪些不会,为什么?
- 有没有其他更好的方法(不包括
sampler2DArray
)?
- 为什么第一个方法继续编译,link,并且没有错误?
我知道我可以使用 sampler2DArray
但我的图片可能有不同的尺寸。
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);
}
}
Why did the first method continue to compile, link, and work with no error?
因为 NVIDIA 将成为 NVIDIA。他们并不真正关心确保您没有意外使用您不应该使用的功能,或者遵循规范的明确措辞。
我正在使用顶点着色器、片段着色器和少量纹理图集(包含数十个较小精灵的图像)将精灵绘制到屏幕上。我的目标是对整个场景使用单个绘制调用,因此需要创建一个可以根据属性动态选择纹理的着色器。每个纹理图集按顺序绑定并发送平面属性 textureid
以确定要使用的纹理,并且该纹理的区域由 uv
.
GLSL 3.30 规范说采样器数组需要一个常量表达式作为索引,但以下编译和 links 没有错误(在最新的 Nvidia 驱动程序上):
#version 330
uniform sampler2D sampler[4];
in vec2 uv;
flat in int textureid;
out vec4 endcolor;
void main() {
endcolor = texture(sampler[textureid], uv);
}
我不能保证这适用于所有硬件,并且很困惑为什么它 linked 没有任何警告。然后我决定尝试以下方法:
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);
}
}
在这三种方法中:
- 哪些会导致未定义或错误的行为,哪些不会,为什么?
- 有没有其他更好的方法(不包括
sampler2DArray
)? - 为什么第一个方法继续编译,link,并且没有错误?
我知道我可以使用 sampler2DArray
但我的图片可能有不同的尺寸。
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);
}
}
Why did the first method continue to compile, link, and work with no error?
因为 NVIDIA 将成为 NVIDIA。他们并不真正关心确保您没有意外使用您不应该使用的功能,或者遵循规范的明确措辞。