我正在开发一个涉及在游戏中获取相机角度的应用程序。角度可以是 0-359 之间的任何位置。0 是北,90 是东,180 是南,等等。我正在使用一个 API,它在 Camera 类中有一个 getAngle() 方法。
我如何找到不同摄像机角度之间的平均值。0 和 359 的实际平均值为 179.5。作为一个摄像机角度,那将是南方,但显然 0 和 359 都非常接近北方。
您可以从向量的角度来考虑它。让和成为你的两个角度,用弧度表示。然后我们可以确定在这些角度的单位向量的 x 和 y 分量:θ1
θ2
x 1 = sin(θ 1 ) y 1 = cos(θ 1 ) x 2 = sin(θ 2 ) y 2 = cos(θ 2 )
然后,您可以添加这两个向量,并确定结果的 x 和 y 分量:
x * = x 1 + x 2 y * = y 1 + y 2
最后,您可以确定这个结果向量的角度:
θ avg = tan -1 (y * /x * )
或者,更好的是,使用atan2
(许多语言支持的功能):
θ avg = atan2(y * , x * )
您可能必须分别处理 where和的情况,因为这意味着这两个向量指向完全相反的方向(那么“平均值”应该是多少?)。y* = 0
x* = 0
这取决于您所说的“平均”是什么意思。但正常的定义是夹锐角的平分线。您必须将两者置于 180 度以内。有很多方法可以做到这一点,但一种简单的方法是增加或减少一个角度。如果角度是a
和b
,那么这将做到:
if (a < b)
while (abs(a - b) > 180) a = a + 360
else
while (abs(a - b) > 180) a = a - 360
现在您可以计算简单平均值:
avg = (a + b) / 2
当然,您可能还想再标准化一次:
while (avg < 0) avg = avg + 360
while (avg >= 360) avg = avg - 360
在您的示例中,您将有 a=0,b=359。第一个循环将 a 增加到 360。平均值为 359.5。当然,如果您愿意,您可以将其四舍五入为整数。如果四舍五入到 360,那么最后一组循环将递减为 0。
请注意,如果您的角度始终归一化为 [0..360),则这些循环都不会执行超过一次。但它们可能是一种很好的做法,因此狂野的论点不会导致您的代码失败。
你想平分角度而不是平均它们。首先获取它们之间的距离,采用最短的方式,然后将其分成两半并添加到其中一个角度。例如:
A = 355
B = 5
if (abs(A - B) < 180) {
Distance = abs(A - B)
if (A < B) {
Bisect = A + Distance / 2
}
else {
Bisect = B + Distance / 2
}
}
else {
Distance = 360 - abs(A - B)
if (A < B) {
Bisect = A - Distance / 2
}
else {
Bisect = B - Distance / 2
}
}
或者类似的东西——对于给定的输入,“Bisect”应该为零。可能有一些聪明的方法可以使算术产生更少的 if 和 abs 操作。
在评论中,您提到要平均的所有“角度”都在 90 度以内。我猜实际上只有一个相机,但它移动很多,并且您正在为相机 POV 创建某种图像稳定性机制。
无论如何,只有相机可能在270-359象限和0-89象限的特殊情况。对于所有其他情况,您可以取一个简单的平均值。因此,您只需要检测这种特殊情况,当它发生时,将 270-359 象限中的角度视为 -90 到 -1。然后,在计算简单平均值后,如有必要,将其调整回 270-359 象限。
在 C 代码中:
int quadrant (int a) {
assert(0 <= a && a < 360);
return a/90;
}
double avg_rays (int rays[], int num) {
int i;
int quads[4] = { 0, 0, 0, 0 };
double sum = 0;
/* trivial case */
if (num == 1) return rays[0];
for (i = 0; i < num; ++i) ++quads[quadrant(rays[i])];
if (quads[0] == 0 || quads[3] == 0) {
/* simple case */
for (i = 0; i < num; ++i) sum += rays[i];
return sum/num;
}
/* special case */
for (i = 0; i < num; ++i) {
if (quadrant(rays[i]) == 3) rays[i] -= 360;
sum += rays[i];
}
return sum/num + (sum < 0) * 360;
}
可以以牺牲目的明确性为代价来优化此代码。当您检测到特殊情况时,您可以在sum
事后修复。因此,您可以计算sum
并找出特殊情况,并一次性完成修复。
double avg_rays_opt (int rays[], int num) {
int i;
int quads[4] = { 0, 0, 0, 0 };
double sum = 0;
/* trivial case */
if (num == 1) return rays[0];
for (i = 0; i < num; ++i) {
++quads[quadrant(rays[i])];
sum += rays[i];
}
if (quads[0] == 0 || quads[3] == 0) {
/* simple case */
return sum/num;
}
/* special case */
sum -= quads[3]*360;
return sum/num + (sum < 0) * 360;
}
我相信它可以进一步优化,但它应该给你一个开始。