【问题标题】:GLSL(330) modulo returns unexpected valueGLSL(330) 取模返回意外值
【发布时间】:2013-05-22 20:49:27
【问题描述】:

我目前正在使用 GLSL 330,并且遇到了 mod() 函数的一些奇怪行为。 我在 Windows 8 下使用 Radeon HD 6470M 工作。我无法在使用 Windows 7 和 GeForce GTX 260 的台式电脑上重现此行为。

这是我的测试代码:

float testvalf = -126;
vec2 testval = vec2(-126, -126);

float modtest1 = mod(testvalf, 63.0);   //returns 63
float modtest2 = mod(testval.x, 63.0);  //returns 63
float modtest3 = mod(-126, 63.0);   //returns 0

编辑:

以下是在 IceCools 建议后完成的更多测试结果。

int y = 63;
int inttestval = -126;
ivec2 intvectest(-126, -126);
float floattestval = -125.9;


float modtest4 = mod(inttestval, 63); //returns 63
float modtest5 = mod(intvectest, 63); //returns vec2(63.0, 63.0)
float modtest6 = mod(intvectest.x, 63); //returns 63
float modtest7 = mod(floor(floattestval), 63); //returns 63
float modtest8 = mod(inttestval, y); //returns 63
float modtest9 = mod(-126, y); //returns 63

我更新了驱动程序并再次测试,结果相同。再次无法在桌面上重现。 根据mod 上的 GLSL 文档,可能的参数组合是 (GenType, float) 和 (GenType, GenType) (没有双精度,因为我们小于 4.0)。返回类型也被强制浮动,但这对于这个问题应该无关紧要。

【问题讨论】:

  • 你能测试一下 int x = -126/63 是否返回 -3 吗?这听起来很奇怪,但 int 除法并不是那么简单,也许 ATI 实现将其降级为浮点除法,并将结果转换回 int。正如我们在 modtest1 (int)(-126.0/63.0) 看到的那样是 -3
  • 我刚刚测试了 -126/63,既通过将它分配给一个变量,也通过测试 if 子句中的返回值。两次结果都是-2。我还测试了 int((-126.0/63.0)) ,它也按预期返回 -2。我的想法慢慢用完了,您的浮点精度错误最符合逻辑,我想不出其他任何可能导致它的原因。
  • 似乎除了精度问题之外的其他事情正在发生,因为对于所有可能的精度和所有可能的 x 和 y 值,不变量 !(abs(mod(x, y)) >= abs(y)) 应该始终为真。打破这打破了图形程序中的mod。 (奇怪的 !...>= 使得它对于 NaN 值也总是正确的)。

标签: opengl glsl modulo opengl-3


【解决方案1】:

我不知道如果你是故意这样做的,但是 -126 是一个 int 而不是一个浮点数,并且代码可能没有按照你的预期进行。

顺便说一下模数: 请注意,调用了 2 个不同的函数:

前两行:

float mod(float, float);

最后一行:

int mod(int, float);

如果我是对的,mod 的计算方式如下:

genType mod(genType x, float y){
return x - y*floor(x/y);
}

现在请注意,如果 x/y 计算结果为 -2.0,它将返回 0,但如果计算结果为 -2.00000001,则返回 63.0。 int/float 和 float/float 除法之间的差异并非不可能。

所以原因可能只是因为您混合使用了整数和浮点数。

【讨论】:

  • 这很好,如果我没记错的话,我认为我已经将 (int, int) 作为具有相同结果的参数进行了测试。不过我可能弄错了。我明天会再次测试它并将结果作为更新发布。
【解决方案2】:

我想我找到了答案。

我一直错的一件事是mangslgenType 关键字并不意味着泛型类型,就像在 c++ 模板中一样。

GenTypefloatvec2vec3vec4 的简写(见link - ctrl+f genType)。

Btw genType 命名是这样的:

genType - floats
genDType - doubles
genIType - ints
genBType - bools 

这意味着genType mod(genType, float)意味着没有像int mod(int, float)这样的功能。

上面所有的代码都在调用float mod(float, float)(幸好函数参数有隐式类型转换,所以mod(int, int)也可以工作,但实际上mod(float, float)被调用了)。

作为证明:

int x = mod(-126, 63);
Doesn't compile: error C7011: implicit cast from "float" to "int"

它只是因为它返回float而不起作用,所以它是这样工作的:

float x = mod(-126, 63);

因此float mod(float, float) 被调用。

所以我们又回到了原来的问题:

  • 浮点除法不准确
  • int to float cast 不准确
  • 在大多数 GPU 上应该不是问题,因为如果浮点数之间的差异小于 10^-5,则认为浮点数相等(它可能因硬件而异,但我的 GPU 就是这种情况)。所以 floor(-2.0000001) 是-2。 Highp 浮点数比这要准确得多。
  • 因此,要么您没有使用 highp 浮点数(precision highp float; 应该修复它),要么您的 GPU 对浮点相等性有更严格的限制,或者某些函数返回的值不太准确。
  • 如果一切都失败了尝试:

    #extension BlackMagic : enable

【讨论】:

  • 非常好的资料,我回家后研究一下我的gpu的浮点精度。如果有什么需要的话,甚至可以联系 AMD 的支持服务。但即使精度是答案,这仍然不能解释为什么你得到 63 für mod(variable, 63.0) 和 0 for mod(-126, 63.0)。
【解决方案3】:

可能某些驱动程序设置强制默认浮点精度为中等

如果发生这种情况,您定义的所有变量都将是mediump,但是在代码中键入的数字仍将保持highp。 考虑这段代码:

precision mediump float;
float x = 0.4121551, y = 0.4121552;
x == y; // true
0.4121551 == 0.4121552; // false, as highp they still differ.

所以mod(-126,63.0) 仍然可以足够精确以返回正确的值,因为它使用高精度浮点数,但是如果你给出一个变量(就像在所有其他情况下一样),它只会是 mediump ,该函数没有足够的精度来计算正确的值,当您查看您的测试时,这就是正在发生的事情:

  • 所有至少采用一个变量的函数都不够精确
  • 唯一接受 2 个类型数字的函数调用会返回正确的值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-08
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2013-06-26
    • 2014-12-11
    • 2016-01-30
    • 1970-01-01
    相关资源
    最近更新 更多