问题描述
我在使用下面的代码限制摄像机俯仰角度(在 -90º 和 90º 之间)时遇到了一个大问题.这有点像 这个问题.
I'm having a big problem limiting the camera pitch angle (between -90º and 90º) with the code below. This is somewhat a follow up to this question.
问题似乎在于相机旋转超过 -90º 或超过 +90º,当发生这种情况时,我会向下(或向上)看,但视图只是围绕 Y 轴旋转了 180º.
The problem, it seems, is that the camera rotates more than -90º or more than +90º and when that happens, I'll be looking down (or up) but but the view just rotated 180º around the Y axis.
示例:我朝北看地平线,我开始往下看,直到我不能再往下看(受以下代码限制).然后我开始抬头看,我会朝南.
Example: I'm facing north looking at the horizon and I start to look down until I can't go down anymore (limited by the code below). Then I start to look up and I'll be facing south.
void Camera::Rotate(Vector3D angle) {
angle = angle * CAMERA_ROTATION_SPEED;
accumPitchAngle += angle.x;
if(accumPitchAngle > 90.0f) {
angle.x = 90.0f - (accumPitchAngle - angle.x);
accumPitchAngle = 90.0f;
}
if(accumPitchAngle < -90.0f) {
angle.x = -90.0f - (accumPitchAngle - angle.x);
accumPitchAngle = -90.0f;
}
// Rotate along the WORLD_SKY_VECTOR axis (yaw/heading rotation)
// WORLD_SKY_VECTOR = (0.0f, 1.0f, 0.0f)
if(angle.y != 0.0f) {
Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
UpVector = Vector3D::CrossProduct(RightVector, Reference);
}
// Rotate along the x axis (pitch rotation)
if(angle.x != 0.0f) {
Reference = RotateArbitraryAxis(Reference, RightVector, angle.x);
UpVector = Vector3D::CrossProduct(RightVector, Reference);
}
// Makes sure all vectors are perpendicular all the time
Reference.Normalize();
RightVector = Vector3D::CrossProduct(Reference, UpVector);
RightVector.Normalize();
UpVector = Vector3D::CrossProduct(RightVector, Reference);
UpVector.Normalize();
}
Vector3D Camera::RotateArbitraryAxis(const Vector3D v, Vector3D u, double angle) {
Vector3D result;
u.Normalize();
double scalar = Vector3D::DotProduct(v, u);
double c = cos(Math::DegreesToRadians(angle));
double s = sin(Math::DegreesToRadians(angle));
double a = 1.0f - c;
result.x = u.x * scalar * a + (v.x * c) + ((-u.z * v.y) + (u.y * v.z)) * s;
result.y = u.y * scalar * a + (v.y * c) + (( u.z * v.x) - (u.x * v.z)) * s;
result.z = u.z * scalar * a + (v.z * c) + ((-u.y * v.x) + (u.x * v.y)) * s;
return result;
}
问题可能出在 if(angle.y != 0.0f)
语句中,如果我注释该代码块,则根本不存在问题. 它与 WORLD_SKY_VECTOR
有关,但该代码是这样的,可以让我旋转航向并保持相机水平.如果我改用 UpVector
,问题就解决了.但这仅适用于飞行模拟器,我需要保持水平线,这就是 WORLD_SKY_VECTOR
背后的原因.但是当我将相机直接指向下方时,这似乎是侧向切换"的原因.
The problem is probably in the if(angle.y != 0.0f)
statement, if I comment that code block, the problem doesn't exist at all. It has something to do with WORLD_SKY_VECTOR
, but that code is like that to allow me to rotate the heading and keep the camera leveled. If I used the UpVector
instead, problem solved. But that is only good for a flight simulator, I need to keep the horizon leveled, that's the reason behind WORLD_SKY_VECTOR
. But it seems that's the cause of "side switching" when I point the camera straight down.
根据下面评论的要求...这是针对第一人称(和第三人称,但我还没有开始实现该部分)相机,当我直视时,-90º(或直视,+90º)并且当角度从 -89º 到 -91º(或从 +89º 到 +91º)时,我希望相机能够防止这种情况发生并且不要超出 -90º、+90º 的限制.当它达到该限制时,我需要相机能够返回(如果我处于 -90º 则向上或如果我处于 +90º 则向下).现在这只是有时有效,其他时候我会面向另一个方向,而不是我最初看的方式.
As per requested on a comment below... This is for a first person (and third person but I haven't started implementing that part yet) camera and when I look straight down, -90º (or straight up, +90º) and when the angle goes from -89º to -91º (or from +89º to +91º) I want the camera to prevent that and don't go beyond the -90º, +90º limit. When it reaches that limit I need the camera to be able to go back (either up if I'm at -90º or down if I'm at +90º). Right now this only works sometimes, other times I'll be facing the other way instead of the way I was initially looking at.
推荐答案
后来我想出了一个非常简单明了的解决方案.就像在答案和评论中多次提到的那样,问题出在前向向量(我的 Reference
)是直向上或向下看时,这意味着该向量与 WORLD_SKY_VECTOR代码>,这就是它变得混乱的原因.
After while I come up with a solution which is pretty simple and straightforward. Like it was said many times in the answers and comments, the problem lies when the forward vector (my Reference
) is looking straight up or down, which means this vector is parallel to WORLD_SKY_VECTOR
and that's reason it gets all messy.
我在这个解决方案背后的想法是,当我直视下方(或向上)并且想要向左或向右旋转时,这实际上是围绕前向矢量的 滚动 旋转.为什么不在俯仰角为-90º或90º时执行滚动运动?
My thought behind this solution is that when I'm looking straight down (or up) and I want to rotate left or right, this is actually a roll rotation around the forward vector. Why not execute a roll movement when the pitch angle is at -90º or 90º instead?
综上所述,我通过简单地用以下代码替换偏航/航向旋转解决了这个问题:
Putting that together, I solved the problem by simply replacing the yaw/heading rotation with the following code:
if(angle.y != 0.0f) {
if(abs(accumPitchAngle) == 90.0f) {
RightVector = RotateArbitraryAxis(RightVector, Reference, -angle.y);
UpVector = Vector3D::CrossProduct(RightVector, Reference);
} else {
Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
UpVector = Vector3D::CrossProduct(RightVector, Reference);
}
}
这似乎是一个相当简单直接的工作解决方案,我可能会将我的答案标记为已接受,除非有人指出我可能不会考虑的针对此实现的任何严重问题.我会等几个小时再做,以便之前的答案得出某种结论.
This seems a fairly simple and straightforward working solution, I'll probably be marking my answer as accepted unless someone points out any strong problem against this implementation that I may not be considering. I'll wait a few hours before doing it, so that the previous answers reach some sort of conclusion.
这篇关于无法使用向量在 OpenGL 中限制 [-90º, 90º] 之间的相机俯仰角!的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!