本文介绍了无法使用向量在 OpenGL 中限制 [-90º, 90º] 之间的相机俯仰角!的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用下面的代码限制摄像机俯仰角度(在 -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º] 之间的相机俯仰角!的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-19 06:15