如何计算在OpenGL/GLUT中用鼠标移动相机的点? [英] How to calculate look at point to move the camera with the mouse in OpenGL/GLUT?

查看:22
本文介绍了如何计算在OpenGL/GLUT中用鼠标移动相机的点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这会让我难以解释,所以请多多包涵.

This will be confusing for me to explain so please bear with me.

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

I've already implemented most type of movements and rotations in my camera class, everything is working with the keyboard, now I want to implement the mouse. I capture the mouse movement like this:

#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() 相机函数,但这是不行的,我必须创建一个额外的函数来旋转两个轴同时".

After everything I've read, I believe this is enough in my situation. However I must state that first I tried to call the Pitch() and Yaw() camera functions but it was a no go, I had to create an extra function to rotate both axis "at the same time".

旋转功能是这样的:

#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 中使用的真实观察点,像这样:

The Reference is the viewing direction, the point the camera is looking at. And since it's a normalized vector, it goes from -1.0 to 1.0. This vector, or point, is later used together with another vector (Position, which is the camera location) to calculate the real look at point to use in gluLookAt, like this:

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
    );
}

+-* 等上面所有的向量操作当然都是重载的.

All vector operations above like +, - and * are overloaded of course.

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

Now I'm going to try to describe my problem...

上面的旋转功能很好用,因为它使用鼠标正确地执行了俯仰和偏航.但是,这些轮换看起来不像第一人称射击游戏中的轮换.在那些游戏中,当一个人看着天空然后向左/向右看时,人们期望继续看天空.想象我们在一个球体内,这样的运动应该在球体的顶部画"一个圆圈.

The rotate function above works just fine in the sense that it correctly performs a pitch and yaw by using the mouse. However, those rotations don't look like the ones in First Person Shooter games. In those games, when one looks at sky and then looks left/right, one expects to keep looking at the sky. Imagining we are inside a sphere, a movement like that should "draw" a circle in the top part of the sphere.

但这不是发生的事情,因为偏航不是这样做的.偏航运动将围绕任意轴旋转,我认为这是这种情况下的向上向量.所以,问题在于偏航运动,因为俯仰似乎工作正常.

But that's not what happens because that's not what a yaw does. A yaw movement will rotate around an arbitrary axis, which I think is the up vector in this situation. So, the problem is in the yaw movement because the pitch seems to work fine.

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

In other words, my code above can't keep the horizon leveled and that's what must happen cause that's happens in games when one looks at the sky and then look left/right, the horizon is always leveled. The same will not happen with my code, I look up and then left/right, and the horizon will be all twisted.

我说得够清楚了吗?我不确定如何更好地解释这一点.:( 希望这足以让任何人理解.

Did I make myself clear enough? I'm not sure how can I explain this any better. :( Hopefully it's enough for anyone to understand.

我不知道如何解决这个问题...如何在向上/向下看后正确地向左/向右看,同时保持水平线?

I'm not sure how can I fix this problem... How can I look left/right correctly after looking up/down, keeping the horizon leveled?

我的旋转函数代码取自同样存在的 Yaw 和 Pitch 函数,因此我可以独立调用这些旋转.出于参考目的,我将在下面添加它们以及 Roll 功能(我可能永远不会使用,但如果我需要它,它就在那里):

My rotate function code is taken from both the Yaw and Pitch functions which also exist so I can call those rotations independently. For reference purposes I'll add them below along with the Roll function too (which I'll probably never use, but in case I need it, it's there):

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);
}

推荐答案

您的问题似乎在语句中:

Your problem appears to be in the statement:

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

在上一个语句中将 Reference 旋转到 RightVector 后,它们的叉积将不再产生为您提供水平地平线的 UpVector.用你的手臂试试.此外,Reference 和 RightVector 没有相隔 90 度,因此 UpVector 甚至都不是单位向量.(最后,为了清楚起见,您实际上应该只切换叉积的顺序,而不是乘以 (-1).)

Once you've rotated Reference toward RightVector in the previous statement, their cross product will no longer result in an UpVector that gives you a horizontal horizon. Try it with your arms. Furthermore, Reference and RightVector are not separated by 90 degrees, so UpVector won't even be a unit vector either. (Finally, you should really just switch the order of the cross product for clarity, rather than multiplying by (-1).)

老实说,如果我这样做,我会采取不同的方法.我看不出两个旋转必须在一个函数中的任何合乎逻辑的原因.在使用向量时,我也会不惜一切代价避免显式正弦和余弦.我认为您真正需要的是关于任意轴旋转的功能.如果不出意外,它非常很有用.幸运的是,所有细节都由默里先生照顾!如果你实现了这个功能,那么它就变得非常简单了.定义一个始终向上的常量 SkyVector.然后在伪代码中,

Honestly, if I were doing it, I would take a different approach. I don't see any logical reason why the two rotations have to be in one function. I also avoid explicit sines and cosines at all costs when working with vectors. I think what you really need is a function to Rotate About an Arbitrary Axis. If nothing else, it's very useful. Fortunately all the details are taken care of by Mr. Murray! If you implement this function, then it becomes very simple. Define a constant SkyVector that always points upward. Then in pseudocode,

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,但我通常做的和你做的差不多.您可能需要不时检查以确保您的向量保持良好和垂直.四元数更稳定.

If you go through that operation by operation, it should hopefully make some sense. Finally, I'll add that quaternions are really the 'correct' way to do this stuff and avoid gimbal lock, but I usually do pretty much exactly what you've done. You might have to check every now and then to make sure your vectors stay nice and perpendicular. Quaternions are more stable.

如果轴旋转功能过大,您仍然可以使用简单的向量和旋转矩阵来实现它.唯一的事情是你必须开始将东西投影到水平面上,这样你就可以独立地进行两次旋转而且它仍然需要一些正弦和余弦.您的时间可能最好花在实现轴旋转功能上!

If the axis rotation function is overkill, you can still implement this with simple vectors and rotation matrices. The only thing is you'll have to start projecting things into the horizontal plane so that you can do the two rotations independently And it'll still take some sines and cosines. Your time is probably better spent implementing the axis rotation function!

这篇关于如何计算在OpenGL/GLUT中用鼠标移动相机的点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆