两个角度插值的逻辑问题?
Logical issue in interpolation between two angles?
我想做的是让一个旋转一定度的 2D 对象,例如 A 度,旋转以面向鼠标,其从该对象的方向是 B 度。这是在openGl中。
我已经能够使用 glRotatef 函数立即旋转对象,但我不想做的是能够在一定秒数内控制旋转。
我正在使用两种增加或减少旋转的方法:
void GameObject::increaseRot(int millis) {
rotation += getRotDeg(millis);
}
void GameObject::decreaseRot(int millis) {
rotation -= getRotDeg(millis);
}
double GameObject::getRotDeg(int millis) {
double rot = 360 / this->rotSpeed;
rot = rot * millis / 1000.0;
return rot;
}
Millis来自一个正常工作的计时器,所以我可以使一个物体以每360度的速度旋转一次。
编辑:我在互联网上找到了一个解决方案,似乎大部分都有效。将该解决方案的公式与我自己的代码一起使用,代码是
shortest_angle=((((end - start) % 360) + 540) % 360) - 180;
/* check which way to rotate
* this part of the code appears to work fine, all it does is
* rotate a certain number of degrees, it's my code that I've been
* using the whole time
*/
if(rotateA < 0)
game.getPlayer().decreaseRot(deltaT);
else if(rotateA > 0)
game.getPlayer().increaseRot(deltaT);
但是,代码在某些值下仍然采用更长的路由,我不知道为什么......
我注意到这种情况发生的值是:
45 trying for 135
225 trying for 315
315 trying for 45
当然,这些是近似值,这些区域周围的任何值都会搞砸。我一直认为这与限制 90、180、270 和 360/0 有关,但我无法弄清楚实际问题是什么。
我已经找到并制定了这个问题的解决方案,适用于可能也有相同问题的其他任何人。
使用"user151496"中的代码,旋转插值
shortest_angle=((((end - start) % 360) + 540) % 360) - 180;
return shortest_angle * amount;
您可以更改它以应用您自己的自定义旋转速度,就像我使用
诸如if(rotateA < 0 && rotateA >= -180)
game.getPlayer().decreaseRot(deltaT);
else if (rotateA < 0 && rotateA <= -180) //for the crossing of the boundary
game.getPlayer().increaseRot(deltaT);
else if(rotateA > 0 && rotateA <= 180)
game.getPlayer().increaseRot(deltaT);
else if(rotateA > 0 && rotateA >= 180)
game.getPlayer().decreaseRot(deltaT); //for the crossing of the boundary
所有这些^所做的是检查shortest_angle是正数还是负数(rotateA)。然后它相应地顺时针旋转(减少腐烂)或逆时针旋转(增加腐烂)。 但是,请注意,检查-180度和180度条件的两行。这些是必要的 - 我发现很难用数字解释,但它与越过 0/360 度线有关。
如果您没有这些额外的条件,则每当您必须越过这样的边界时,旋转将以更大的角度旋转。
可以在不使用任何条件分支的情况下插值角度,如下所示:
template <typename T>
T lerp_fixed_degrees(T a, T b, T x) {
T d = wrap_degrees(b - a);
return wrap_degrees(a + std::copysign(std::fmin(std::abs(x), std::abs(d)), d));
}
- 使用
wrap_degrees(b - a)
计算范围内a
和b
之间的差[-180, 180)
。该函数wrap_degrees
计算[-180, 180)
范围内的等效角度(例如wrap_degrees(190) == -170
和wrap_degrees(-190) == 170
)。 - 使用
std::fmin(std::abs(x), std::abs(d))
确定步长和差值的较小者。这可以防止我们超调目标(如果您知道x
是积极的,您可以使用x
而不是std::abs(x)
,但我在这里选择了稳健性)。 - 使用
std::copysign
在正确的方向上插值。 - 将结果添加到起始角度,并返回范围
[-180, 180)
中的等效角度。
函数wrap_degrees
实现如下:
template <typename T>
T wrap_degrees(T x) {
return fmod_floor(x + T(180), T(360)) - T(180);
}
- 将
180
添加到输入值。 - 使用
fmod_floor
将值包装在范围[0, 360)
中。fmod_floor
函数与std::fmod
类似,只是它产生地板除法的余数,它总是与除数具有相同的符号。这是我们想要的行为(例如fmod(-10, 360) == -10
而fmod_floor(-10, 360) == 350
)。 - 通过减去
180
将换行的值移回[-180, 180)
范围(这由前面添加的180
平衡)。
fmod_floor
函数的实现方式如下:
template <typename T>
T fmod_floor(T a, T n) {
return std::fmod(std::fmod(a, n) + n, n);
}
- 确保值在与
std::fmod
(-n, n)
的范围内。 - 将值移动到范围
(0, 2n)
。 - 确保该值再次在
std::fmod
[0, n)
范围内。
下面是一个快速演示:
double a = 90.;
double prev;
do {
std::cout << a << "n";
prev = a;
a = lerp_fixed_degrees(a, -100., 45.);
} while (a != prev);
输出:
90
135
-180
-135
-100
您还可以使用从0
到1
的值执行简单的线性插值,如下所示:
template <typename T>
T lerp_degrees(T a, T b, T t) {
return wrap_degrees(a + wrap_degrees(b - a) * t);
}
我认为这个是不言自明的。
- 我的固定时间步长与增量时间和插值的解决方案是错误的吗?
- 将使用太多的纹理插值器 - 带旋转的着色器
- 升压插值条件变量可以虚假唤醒吗?
- 如何获得插值极坐标?
- 如何线性插值到不恒定的目的地
- 向心Catmull-Rom样条曲线插值alpha参数
- 使用 OpenGL 插值数据缓冲区?
- 缩放和插值数组
- 骨骼动画:变换矩阵(collada)之间的插值
- 从一组点C++平面插值
- 从 C++14 到 C++98 的端口字符串插值
- 将矢量的数据扩展到指定大小(插值)
- 双线性插值实现的错误
- 如何避免线性插值"trap"?
- 使用 DirectX 11 插值背景颜色?
- OpenGL 缺陷 - 不需要的插值
- 如何在三角形曲面细分评估着色器中插值 UV 映射坐标?
- 将 N 个公式替换为一个(字符串插值)
- 两个角度插值的逻辑问题?
- 拉盖尔插值算法,我的实现有问题