iPhone 6 Plus上的GLSL atan函数结果不正确

5
考虑以下片段着色器。当我在模拟器或iPhone 5(8.3)上运行它时,它显示了期望的颜色(红色)。如果我在iPhone 6 Plus(8.2)上运行它,则会进入第二个if子句(绿色),这显然是错误的,因为计算的正确结果应该约为1.22。如果我直接提供atan参数作为编译时常数,则计算有效。
我已经在最新的Xcode 6.2和最新的6.3 Beta上尝试过。
void main()
{
    highp float y = 0.57;
    highp float x = 0.21;

    highp float a = atan(y, x); 
    // works if changed to atan(0.57, 0.21);
    // does not work if changed to atan(y / x);

    if (a > 1.2 && a < 1.25)
    {
        // Should always come here
        // Works on Simulator, iPhone 5
        gl_FragColor = vec4(1, 0, 0, 1);
    }
    else if (a > 1.5 && a < 1.55)
    {
        // Comes here on iPhone 6 Plus
        gl_FragColor = vec4(0, 1, 0, 1);
    }

    return;
}

更新:情况变得更加复杂。

如果我按以下方式更改着色器,错误就会消失,并且atan返回正确的值。似乎我放到x和y中的任何内容都必须是编译时常量之外的其他东西。这也解决了我的生产代码中的问题,因为我根本没有编译时常量。顺便说一下,acos和asin也存在同样的问题。

highp float y = texCoord.x;
highp float x = texCoord.x;

if (x == x) // Removing this line makes error return
{
    x = 0.21;
}

if (y == y) // Removing this line makes error return
{
    y = 0.57;
}

另一个例子。这是我实际使用的方法。

如果我按照这种方式进行计算,它在 iPhone 6 上会失败:

float y = sin(lon2 - lon1) * cos(lat2);
float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);

float bearing = atan(y, x) + 2.0 * pi;

return mod(bearing, 2.0 * pi);

如果我这样做,它就可以工作:

float y = texCoord.y;
float x = texCoord.x;

if (y == y)
{
    y = sin(lon2 - lon1) * cos(lat2);
}

if (x == x)
{
    x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
}

float bearing = atan(y, x) + 2.0 * pi;

return mod(bearing, 2.0 * pi);

兴趣所在,当 x < y 时,pi / 2 - tan(x, y) 是否给出您期望的结果? - James Snook
不,它会给出完全相同(错误的)结果。 - Seppo
2个回答

1
这似乎是一个编译时常量传播错误。
你的 if (x == x) 子句看起来可以被编译器消除,但由于它是一个 fp 值,它实际上是一个针对 NaN 的测试。
也就是说,将任何东西与 NaN 进行比较的结果始终为 false,即使你将该值与自身进行比较。
看起来这个测试正在阻止编译器传播你的常量值,如果你要求编译输出汇编代码,这一点应该是显而易见的。

1

我也在iOS13上遇到了一个与atan有关的bug。我使用这个方法来计算两点之间的角度。

float getAngle(vec2 point, vec2 center) {
  float y = point.y - center.y;
  float x = point.x - center.x;
  return atan(y, x);
}

每当我使用相同的点值调用此方法时,它会出现错误。
vec2 p1 = vec2(40.0, 30.0);
vec2 p2 = vec2(40.0, 30.0);
float angle = getAngle(p1, p2);

似乎调用atan(0.0, 0.0)会出现错误

我在iOS上使其正常工作的方法是将方法更改为以下内容

float getAngle(vec2 point, vec2 center) {
  float y = point.y - center.y;
  float x = point.x + 0.000000001 - center.x;
  return atan(y, x);
}

添加0.000000001可以确保两个点之间的减法不会等于0。


这刚好解决了我在iOS上遇到的问题,谢谢! - Kelly Milligan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接