texel中心坐标处的textureGather()行为

时间:2017-09-25 20:35:36

标签: opengl glsl

假设我有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)

1 个答案:

答案 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的幂。