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

2023-12-29

这对我来说解释起来会很混乱,所以请耐心等待。

我已经在我的相机类中实现了大多数类型的移动和旋转,一切都可以通过键盘进行,现在我想实现鼠标。我像这样捕获鼠标移动:

#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() and Yaw()相机功能,但这是行不通的,我必须创建一个额外的功能来“同时”旋转两个轴。

旋转函数是这样的:

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

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

上面的所有向量运算都像+, - and *当然是超载的。

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

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

但事实并非如此,因为偏航不是这么做的。偏航运动将围绕任意轴旋转,我认为在这种情况下它是向上向量。所以,问题出在偏航运动上,因为俯仰似乎工作得很好。

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

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

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

EDIT:

我的旋转函数代码取自同样存在的偏航和俯仰函数,因此我可以独立调用这些旋转。出于参考目的,我也会将它们与 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);
}

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

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

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

老实说,如果我这样做,我会采取不同的方法。我看不出为什么两次轮换必须在一个函数中的任何逻辑原因。在使用向量时,我还会不惜一切代价避免显式正弦和余弦。我认为你真正需要的是一个功能绕任意轴旋转 http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.html。如果不出意外的话就是very有用。幸运的是,所有的细节都被穆雷先生照顾到了!如果你实现了这个功能,那么事情就变得非常简单了。定义一个常量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 );
}

如果你逐一进行操作,它应该会有意义。最后我要补充一点四元数 http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation确实是做这件事并避免的“正确”方法万向节锁 http://en.wikipedia.org/wiki/Gimbal_lock,但我通常会做与你所做的几乎完全相同的事情。您可能需要时不时地检查一下,以确保矢量保持良好且垂直。四元数更稳定。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在OpenGL/GLUT中计算用鼠标移动相机的观察点? 的相关文章

  • glutPostRedisplay 不在循环内工作

    我试图让一个人在 y 轴上跳跃 所以我使用 2 秒的循环 第一秒它应该向下移动并弯曲膝盖 第二秒它应该向上移动 然后在起始位置完成 现在我刚刚开始让这个人在第一秒内跪下并弯曲膝盖 我还没有编写动画的其余部分 问题是 glutPostRedi
  • 在无头模式下独立运行 Unity,同时捕获屏幕截图

    我需要创建一个在无头模式下运行的统一项目 使用 batchmode 命令 但它必须捕获屏幕截图 例如每一秒并将它们写到一个文件中 我知道在无头模式下 您需要强制调用 Camera Render 才能渲染任何内容 在捕获第一个屏幕截图后 时间
  • 基于鼠标位置的平滑滚动(Jquery)

    HI 我想创建一个基于鼠标位置的平滑滚动条 这个想法是创建一个具有固定宽度的外部 div 内容非常宽 必须根据鼠标位置向左或向右滚动 如果内容是 无限 或 无尽 的 那就太好了 内容是一个非常宽的图像 无缝 地重复 有人可以帮我用 jQue
  • 我的绘图存在坐标/glortho 问题

    I have made a bit of a change to my code in the last couple of hours as everything was messy with my grid so I made it i
  • Three.js 设置并读取相机外观向量

    而不是使用camera rotation或lookAt 函数旋转相机 我想将外观矢量直接传递给相机 是否可以直接设置相机外观矢量以及是否可以从相机读取外观矢量 相机没有 外观矢量 因此无法设置它 但是 您可以构造一个point通过将您的外观
  • 不理解 gluOrtho2D 函数

    我不能做什么gluOrtho2D 函数是做什么的 是否将原点固定在 OpenGL 窗口上的某个特定点或其他位置 这是因为gluOrtho2D 1 1 1 1 将原点固定在窗口的中间 如果它在某个时刻没有修复原点 那么有什么方法可以修复原点
  • 如何将点光源转换为卵形/椭圆形?

    我希望通过具有不同 x 和 y 值的 vec2 半径将当前的圆形光变成椭圆形 有没有办法根据我当前在片段着色器中的代码来做到这一点 uniform struct Light vec4 colour vec3 position vec2 ra
  • 简单的线框格式?

    我正在寻找一种用于线框模型的简单文件格式 我知道 VRML u3D 等 但这些对于我的需求来说似乎很重要 我的标准是 必须有明确的规格 要么是开放的 要么是非常完善 记录的 我只需要 想要 简单的模型 顶点和边 我不想处理面孔或物体 如果格
  • 手电筒打开时 Android 相机的奇怪行为

    我有以下 android 代码 这里用伪代码编写 mCamera configAndInitialize all I want to do before taking picture mCamera startPreview mCamera
  • OpenGL 着色器不与着色器程序链接

    我正在尝试使用 GLFW GLEW 添加着色器 我收到一个错误 指出着色器已加载 但它们没有有效的对象代码 这是我用于加载着色器的代码 class SHADER public void LoadShaders const char vert
  • 使用 GLSL 着色器在同一片段着色器中定义的多个子例程类型无法正常工作

    我正在开发一个使用 GLSL 着色器的程序 我编写了 2 种不同的方法来用 2 种不同的方法计算 ADS 环境光 漫反射 镜面反射 着色 为了正确完成这项工作 我使用子例程来使用一种或另一种方法来计算 ADS 着色 这是片段着色器代码的一部
  • 将像素传递给 glTexImage2D() 后会发生什么?

    例如 如果我创建一个像素数组 如下所示 int getPixels int pixels new int 10 pixels 0 1 pixels 1 0 pixels 1 1 etc glTexImage2D getPixels glTe
  • JavaFX中如何获取鼠标位置?

    我是java fx 的初学者 如何在 JavaFX 中获取鼠标在 x 和 y 中的位置 我尝试使用 AWTMouseInfo 也导入了它 但它不起作用 我还在 Ensembles 中看到了它的代码 在 高级阶段 拖动球窗口 这就是我需要做的
  • 如何在控制台程序中获取鼠标位置?

    如何在 Windows 控制台程序中用 C 获取鼠标单击位置 点击时返回鼠标位置的变量 我想用简单的文本命令绘制一个菜单 这样当有人点击时 游戏就会注册它并知道位置 我知道如何做我需要做的一切 除了单击时获取鼠标位置 您需要使用 Conso
  • 为什么OpenGL使用float而不是double? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • GLSL NVidia 方形神器

    当 GLSL 着色器在以下 GPU 上生成不正确的图像时 我遇到了问题 GT 430 GT 770 GTX 570显卡760 但在这些上正常工作 英特尔高清显卡 2500英特尔高清4000英特尔4400显卡740MRadeon HD 631
  • Ionic-Angular.js 拍照并发送到服务器:空图像

    因此 我设法使用自定义指令通过 Angular js 将图像上传到我的服务器 我还成功地实现了 Cordova 的相机功能 现在我尝试连接两者 但是当将图像发送到服务器时 它们被存储为空 我认为问题在于我使用输入字段来获取图像 并且它获取了
  • 如何将相机中的图像保存到 iPhone 图库中的特定文件夹?

    嘿 我是 iPhone 新手 最近我一直在尝试制作一个应用程序 基本上 我想要做的是 如果用户将从相机捕获任何图像 那么它应该保存在设备库中 我知道如何将照片保存在图库中 它对我有用 但我无法将所有捕获的图像保存到设备图库中的特定文件夹 例
  • 为什么条件变量不可 MoveAssignable

    为什么是一个condition variable不可移动构造 根据http en cppreference com w cpp thread condition variable http en cppreference com w cpp
  • 在 2D 纹理上绘制的红色矩形在绘制后立即消失

    跟随我的另一个问题 https stackoverflow com questions 18477291 render an outlined red rectangle on top a 2d texture in opengl 1847

随机推荐