如何计算在OpenGL / GLUT中使用鼠标移动相机的点?

时间:2011-03-13 05:27:20

标签: opengl camera mouse move glut

这对我来说很难解释所以请耐心等待。

我已经在我的相机类中实现了大多数类型的移动和旋转,一切都在使用键盘,现在我想实现鼠标。我像这样捕捉鼠标移动:

#define SENSITIVITY 25.0f

void main(void) {
    (...)
    glutPassiveMotionFunc(processPassiveMotion);    
    glutWarpPointer(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2);
    glutSetCursor(GLUT_CURSOR_NONE);
    (...)
}

void processPassiveMotion(int x, int y) {
    int centerX = WINDOW_WIDTH / 2;
    int centerY = WINDOW_HEIGHT / 2;

    int deltaX = -1 * (x - centerX);
    int deltaY = -1 * (y - centerY);

    if(deltaX != 0 || deltaY != 0) {
        mainCamera.Rotate(deltaX / SENSITIVITY, deltaY / SENSITIVITY);

        glutWarpPointer(centerX, centerY);
    }
}

在我读过的所有内容之后,我相信这在我的情况下已经足够了。但是我必须声明,首先我尝试调用Pitch()Yaw()相机功能,但这是不行,我必须创建一个额外的功能来“同时”旋转两个轴。 / p>

旋转功能是这样的:

#define DEG2RAD(a) (a * (M_PI / 180.0f))
#define SINDEG(a)  sin(DEG2RAD(a))
#define COSDEG(a)  cos(DEG2RAD(a))

void Camera::Rotate(GLfloat angleX, GLfloat angleY) {
    Reference = NormalizeVector(
        Reference * COSDEG(angleY) + UpVector * SINDEG(angleY)
    );

    Reference = NormalizeVector(
        Reference * COSDEG(angleX) - RightVector * SINDEG(angleX)
    );

    UpVector = CrossProduct(&Reference, &RightVector) * (-1);
    RightVector = CrossProduct(&Reference, &UpVector);
}

Reference是观察方向,即相机正在观察的点。因为它是一个归一化的向量,所以从-1.0到1.0。此向量或点稍后与另一个向量(Position,即相机位置)一起使用,以计算gluLookAt中要使用的点的真实外观,如下所示:

void Camera::LookAt(void) {
    Vector3D viewPoint = Position + Reference;

    gluLookAt(
        Position.x, Position.y, Position.z,
        viewPoint.x, viewPoint.y, viewPoint.z,
        UpVector.x, UpVector.y, UpVector.z
    );
}

上面的所有向量操作(例如+-*当然都会超载。

现在我要尝试描述我的问题......

上面的旋转功能在使用鼠标正确执行俯仰和偏航的意义上可以正常工作。然而,那些旋转看起来不像第一人称射击游戏中的旋转。在那些游戏中,当人们看着天空然后向左/向右看时,人们期望继续看着天空。想象我们在一个球体内,这样的运动应该在球体的顶部“画”一个圆圈。

但事实并非如此,因为这不是偏航所造成的。偏航运动将绕任意轴旋转,我认为这是这种情况下的向上矢量。因此,问题在于偏航运动,因为音高似乎工作得很好。

换句话说,我上面的代码无法保持地平线水平,这就是必须发生的事情,因为当人们看着天空然后向左/向右看,地平线总是被拉平时,会发生在游戏中。我的代码不会发生同样的情况,我向上看,然后向左/向右,地平线将全部扭曲。

我是否足够清楚了?我不知道如何更好地解释这一点。 :(希望它足以让任何人理解。

我不知道如何解决这个问题...在向上/向下看之后我怎样才能正确地向左/向右看?保持地平线水平?

修改

我的旋转功能代码来自偏航和俯仰功能,它们也存在,所以我可以独立调用这些旋转。出于参考目的,我将在下面添加它们以及Roll功能(我可能永远不会使用它,但如果我需要它,它就在那里):

void Camera::Pitch(GLfloat angle) {
    Reference = NormalizeVector(
        Reference * COSDEG(angle) + UpVector * SINDEG(angle)
    );

    UpVector = CrossProduct(&Reference, &RightVector) * (-1);
}

void Camera::Yaw(GLfloat angle) {
    Reference = NormalizeVector(
        Reference * COSDEG(angle) - RightVector * SINDEG(angle)
    );

    RightVector = CrossProduct(&Reference, &UpVector);
}

void Camera::Roll(GLfloat angle) {
    RightVector = NormalizeVector(
        RightVector * COSDEG(angle) - UpVector * SINDEG(angle)
    );

    UpVector = CrossProduct(&Reference, &RightVector) * (-1);
}

1 个答案:

答案 0 :(得分:4)

您的问题似乎出现在声明中:

UpVector = CrossProduct(&Reference, &RightVector) * (-1);

在前一个语句中将Reference引用到RightVector后,它们的交叉产品将不再导致UpVector为您提供水平视野。试试你的手臂。此外,Reference和RightVector没有相隔90度,因此UpVector也不是单位矢量。 (最后,为了清晰起见,您应该切换交叉产品的顺序,而不是乘以(-1)。)

老实说,如果我这样做,我会采取不同的方法。我没有看到为什么两个旋转必须在一个函数中的任何逻辑原因。在使用向量时,我也不惜一切代价避免显式的正弦和余弦。我认为你真正需要的是Rotate About an Arbitrary Axis的功能。如果没有别的,它的非常有用。幸运的是,所有的细节都由穆雷先生照顾!如果实现此功能,则变得非常简单。定义始终指向上方的常量SkyVector。然后是伪代码,

AxisRotation( Vector vec, Vector axis, float angle ) {
    Vector result;

    // The axis is assumed to be normalized:  
    //    (just make sure you're not modifying the original)
    axis = NormalizeVector( &axis );

    // expanded for clarity:
    float u = axis.x;
    float v = axis.y;
    float w = axis.z;
    float x = vec.x;
    float y = vec.y;
    float z = vec.z;
    float c = cos(angle);
    float s = sin(angle);

    // Apply the formula verbatim from the linked page:
    result.x = u*(u*x + v*y + w*z)*(1.-c) + x*c + (-w*y + v*z)*s;
    result.y = v*(u*x + v*y + w*z)*(1.-c) + y*c + ( w*x - u*z)*s;
    result.z = w*(u*x + v*y + w*z)*(1.-c) + z*c + (-v*x + u*y)*s;

    return result;
}

Yaw(angleX) {
    Reference = AxisRotation( &Reference, &SkyVector, angleX );
    RightVector = NormalizeVector( CrossProduct( &Reference, &SkyVector ) );
    UpVector = CrossProduct( &RightVector, &Reference );
}

Pitch(angleY) {
    Reference = AxisRotation( &Reference, &RightVector, angleY );
    //RightVector doesn't change!
    UpVector = CrossProduct( &RightVector, &Reference );
}

如果你通过操作完成该操作,它应该有所帮助。最后,我要补充一点,quaternions真的是'正确'的方式来做这些事情并避免使用gimbal lock,但我通常会完全按照你所做的去做。你可能不得不每时检查一下,以确保你的矢量保持良好和垂直。四元数更稳定。

编辑:如果轴旋转功能过度,您仍然可以使用简单的矢量和旋转矩阵实现此功能。唯一的事情是你必须开始将东西投射到水平面上,这样你就可以独立进行两次旋转而且它仍然需要一些正弦和余弦。你的时间可能更好地用于实现轴旋转功能!