在单一平面内获取两个向量夹角的高效方法是什么?

4
如果我确定向量的x和z值是相同的,因此我只关心测量y平面差异中的“垂直”角度,那么相比计算点积有更有效的方法吗?
使用点积方法的我的当前代码如下:
float a_mag = a.magnitude(); 
float b_mag = b.magnitude();
float ab_dot = a.dot(b);
float c = ab_dot / (a_mag * b_mag);

// clamp d to from going beyond +/- 1 as acos(+1/-1) results in infinity
if (c > 1.0f) {
    c = 1.0;
} else if (c < -1.0) {
    c = -1.0;
}

return acos(c);

我希望能够摆脱这些平方根。


1
一个微妙的问题:acos(1) == 0acos(-1) == PI,它们都不是无穷大。函数 acos 在区间 [-1, 1] 内被定义,超出此区间,acos未定义的 - Sufian Latif
我要问一个非常基础的问题。你确定你需要一个角度吗?很多时候,当我得到一个角度时,要么我立即将其转换回向量,要么直接使用代数解会更好地解决我的问题。 - Michael Dorgan
我理解你的意思。我正在使用这个角度来确定它和另一个角度是否超过了阈值。基本上,它用于将第三人称摄像机的垂直旋转限制在玩家上方或下方的+90度和-90度之间。 - kbirk
2个回答

3
假设你有两个向量u = (x, y1, z)v = (x, y2, z),并且你想计算它们在由这两个向量张成的平面上的夹角。你需要计算点积和模长,但是你可以省略一些操作:
u.v = x.x + y1.y2 + z.z
u^2 = x.x + y1.y1 + z.z
v^2 = x.x + y2.y2 + z.z

因此,我们应该预先计算:

float xz = x*x + z*z, y11 = y1*y1, y12 = y1*y2, y22 = y2*y2;

float cosangle = (xz + y12) / sqrt((xz + y11) * (xz + y22));
float angle = acos(cosangle);

$v^2 = x.x + y2.y2 + z.z$,是吧? - Sufian Latif
@FlopCoder:是的 - 复制/粘贴并不总是完全可靠 :-S - Kerrek SB
太棒了!一个sqrt()已经完成了!现在在另一个函数中,我做同样的事情,但这种情况下,我也知道其中一个y值将始终为0,因此不需要y12和y22变量。所以它变成了cosangle = xz / sqrt((xz + y11) * xz); 有没有办法消除最后的sqrt()呢? - kbirk
1
@user785259:我怀疑,至少如果你想要实际的角度而不是角度平方的话是不能做到的。不过最终目的是什么?也许你可以在其他地方节省一些昂贵的操作...(当然你也了解约翰·卡马克疯狂的倒数平方根方法吧...) - Kerrek SB
我其实并不知道... 我一定会去看看那个算法!非常感谢! - kbirk

1
如果x和z的值没有改变,那么计算就非常简单:只需使用基本三角学。
设点为(x, y1, z)(x, y2, z)。您可以找出向量与ZX平面成的角度。让角度分别为t1t2。然后:
w = sqrt(x^2 + z^2)
tan(t1) = y1 / w
So t1 = atan(y1 / w)
Similarly t2 = atan(y2 / w)
The angle is (t2 - t1)

有一个陷阱:当x和z都为零时,tan是未定义的...但这样一个微不足道的情况可以轻松地单独处理。

不幸的是,似乎没有办法避免平方根。


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