假设我有100x100纹理,我会执行以下操作:
vec4 texelQuad = textureGather(sampler, vec2(50.5)/vec2(100.0));
我请求的坐标正好在texel的中心(50,50)。那么,我将获得由(49,49)和(50,50)限制的四边形纹素或由(50,50)和(51,51)限定的纹素。规范在这个问题上是回避的。它只是陈述如下:
应用LINEAR缩小过滤器的规则 识别四个选定的纹素。
spec的相关部分8.14.2 Coordinate Wrapping and Texel Selection
也不是非常清楚。我最好的假设如下:
ivec2 lowerBoundTexelCoord = ivec2(floor(textureCoord * textureSize - 0.5));
这个假设在实践中是否成立?不,它没有。事实上,没有其他假设也可以,因为不同的硬件会针对这种特殊情况返回不同的结果:
textureSize: 100x100
textureCoord: vec2(50.5)/vec2(100.0)
Hyphotesis: (49, 49) to (50, 50)
GeForce 1050 Ti: (49, 49) to (50, 50)
Intel HD Graphics 630: (50, 50) to (51, 51)
另一个案例:
textureSize: 100x100
textureCoord: vec2(49.5)/vec2(100.0)
Hyphotesis: (48, 48) to (49, 49)
GeForce 1050 Ti: (49, 49) to (50, 50)
Intel HD Graphics 630: (48, 48) to (49, 49)
由于纹素中心坐标处的不可预测的行为,是否会使textureGather()无效?一点也不!。虽然您可能无法预测在某些特定情况下它将返回哪4个纹素,但您仍然可以强制它返回您想要的纹素,方法是在您想要的4个纹素之间设置一个坐标。也就是说,如果我想要由(49,49)和(50,50)限制的纹素,我会打电话:
textureGather(sampler, vec2(50.0, 50.0)/textureSize);
由于我要求此时间的坐标是4个纹素符合的点,因此任何实现都一定会返回给我4个纹素。
现在,问题是:我的分析是否正确?每个使用textureGather()的人都会强制它返回一个特定的四边形纹素,而不是找出它会自己返回哪些纹素吗?如果是这样,那就太遗憾了,它没有反映在任何文档中。
修改
有人指出,OpenGL并不能保证在不同硬件上划分相同浮点数的相同结果。因此,有必要提一下,在我的实际代码中,我有vec2(50.5)/vec2(textureSize(sampler, 0))
而不是vec2(50.5)/vec2(100.0)
。这很重要,因为textureSize()
的存在阻止了在着色器编译时执行该划分。
让我再说一下这个问题:
假设您从黑匣子中获得了标准化的纹理坐标。然后将该坐标传递给textureGather():
vec2 textureCoord = takeFromBlackBox();
vec4 texelQuad = textureGather(sampler, textureCoord);
任何人都可以生成GLSL代码,该代码将返回texelQuad [3]中返回的纹素的整数坐标对,这是2x2盒子的下限角落吗?以下明显的解决方案在所有情况下都不起作用:
vec2 textureDims = textureSize(sampler, 0);
ivec2 lowerBoundTexelCoord = ivec2(floor(textureCoord * textureDims - 0.5));
上述方法可能失败的棘手案例包括:
vec2 textureCoord = vec2(49.5)/vec2(textureSize(sampler, 0));
vec2 textureCoord = vec2(50.5)/vec2(textureSize(sampler, 0));
其中textureSize(sampler, 0)
返回ivec2(100, 100)
。
答案 0 :(得分:3)
回想一下,GL_LINEAR
([OpenGL 4.6(核心)§8.14纹理缩小]的纹素位置由以下公式选择:
i 0 = wrap(⌊u' - 1 /2⌋)
j 0 = wrap(⌊v' - 1 /2⌋)
...
在这种情况下,(u',v')的值等于
(vec2(50.5) / vec2(100)) * vec2(100)
但请注意,这不能保证等于vec2(50.5)
。见The OpenGL Shading Language 4.60 §4.71 Range and Precision:
a / b,1.0 / b:2.5 ULP,b范围为[2 -126 ,2 126 ]。
因此,u'的值可能略大于50.5,略小于50.5,或者可能是50.5。没有承诺!嗯,规格承诺不超过2.5 ULP,但这没什么好写的。您可以看到,当您减去0.5并且发言时,您将要获得49或50,具体取决于数字的舍入方式。
i 0 = wrap(⌊(50.5 / 100)* 100 - 1 /2⌋)
i 0 = wrap(⌊(.505±error)* 100 - 1 /2⌋)
i 0 = wrap(⌊50.5±error - 1 /2⌋)
i 0 = wrap(⌊50±error⌋)
i 0 = 50(如果错误> = 0)或49(如果错误< 0)
所以实际上不是textureGather()
行为不可预测。当您尝试除以100时,不可预测的部分是舍入误差,这在GLSL规范和经典文章 WhatEvery Computer Scientist Should Know About Floating-Point Numbers中进行了解释。
或换句话说,textureGather()
总是会给您相同的结果,但50.5/100.0
却没有。
请注意,如果纹理是2的幂,则可以获得精确的结果,因为您可以使用50.5 * 0.0078125
来计算50.5 / 128
,并且结果将完全正确,因为乘法正确舍入并且0.0078125是2的幂。