首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在GLSL上的鲁棒atan(y,x)将XY坐标转换成角度

在GLSL上的鲁棒atan(y,x)将XY坐标转换成角度
EN

Stack Overflow用户
提问于 2014-09-27 01:27:09
回答 5查看 16.6K关注 0票数 17

在GLSL (特别是我使用的3.00 )中,有两个版本的atan()atan(y_over_x)只能返回- PI /2、PI/2之间的角度,而atan(y/x)可以考虑所有4个象限,因此角度范围涵盖了从-PI、PI到C++中的所有东西。

我想使用第二个atan将XY坐标转换为角度。然而,GLSL中的atan(),除了不能处理x = 0时,也不是很稳定。特别是在x接近于零的情况下,除法会导致相反的结果角(在-PI/2附近得到近似于PI/2的值)。

什么是一个好的,简单的实现,我们可以构建在GLSL atan(y,x)之上,以使它更加健壮?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-09-27 01:27:09

我要回答我自己的问题来分享我的知识。我们首先注意到,当x接近于零时,不稳定性就会发生。但是,我们也可以将其翻译为abs(x) << abs(y)。首先,我们将平面(假设我们位于一个单位圆上)划分为两个区域:一个是|x| <= |y|区域,另一个是|x| > |y|区域,如下所示:

我们知道,atan(x,y)在绿色区域要稳定得多--当x接近于零时,我们只是有接近atan(0.0)的东西,这在数值上是非常稳定的,而通常的atan(y,x)在橙色区域则更稳定。你也可以说服自己,这种关系:

代码语言:javascript
运行
复制
atan(x,y) = PI/2 - atan(y,x)

对于所有未定义的非原点(x,y),我们讨论的是atan(y,x),它能够在整个-PI、PI、而不是只返回-PI/2、PI/2之间的角度的范围内返回角值。因此,我们针对GLSL的健壮atan2()例程非常简单:

代码语言:javascript
运行
复制
float atan2(in float y, in float x)
{
    bool s = (abs(x) > abs(y));
    return mix(PI/2.0 - atan(x,y), atan(y,x), s);
}

顺便提一下,数学函数atan(x)的恒等式实际上是:

代码语言:javascript
运行
复制
atan(x) + atan(1/x) = sgn(x) * PI/2

这是正确的,因为它的范围是(-PI/2,PI/2)。

票数 14
EN

Stack Overflow用户

发布于 2015-07-10 20:00:02

取决于您的目标平台,这可能是一个解决问题。atan(y, x)指定它应该在所有象限中工作,只有当x和y都是0时,才会有未定义的行为。

因此,会期望任何良好的实现都能在所有轴附近稳定,因为这就是2参数atan (或atan2)背后的全部目的。

发问者/应答者是正确的,因为有些实现确实采用快捷方式。但是,接受的解决方案假设,当x接近于零时,坏的实现总是不稳定的:在某些硬件(例如我的Galaxy S4 )中,当 x 接近于零时,值是稳定的,而当 y 接近于0E 216时,则不稳定。

为了测试GLSL呈现器的atan(y,x)实现,下面是一个WebGL测试模式。按照下面的链接,只要您的OpenGL实现是体面的,您就会看到如下所示:

使用本机atan(y,x)**:** http://glslsandbox.com/e#26563.2的测试模式

如果一切顺利,您应该看到8种不同的颜色(忽略中心)。

链接的示例为x和y的几个值(包括0、非常大和非常小的值)提供了atan(y,x)示例。中心框是atan(0.,0.)--数学上没有定义,实现也各不相同。我在我测试过的硬件上看到了0(红色)、PI/2 (绿色)和NaN (黑色)。

下面是可接受的解决方案的测试页面。注意:主机的WebGL版本缺少mix(float,float,bool),所以我添加了一个与规范相匹配的实现。

使用atan2(y,x) 的测试模式(来自公认答案: http://glslsandbox.com/e#26666.0 )

票数 12
EN

Stack Overflow用户

发布于 2014-12-01 12:51:47

x=y=0的情况下,建议的解决方案仍然失败。这里,两个atan()函数都返回NaN。

此外,我也不希望在这两种情况下进行混合。我不确定这是如何实现/编译的,但是IEEE浮动规则x*NaN和x+NaN的结果在NaN中再次出现。因此,如果您的编译器确实使用了mix/插值,那么结果应该是NaN,用于x=0y=0

下面是另一个解决问题的方法:

代码语言:javascript
运行
复制
float atan2(in float y, in float x)
{
    return x == 0.0 ? sign(y)*PI/2 : atan(y, x);
}

x=0时,角度可以是±π/2,两者中的哪一个只依赖于y。如果y=0也是,角度可以是任意的(向量的长度为0)。在这种情况下,sign(y)返回0,这很好。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26070410

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档