如何在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中计算用鼠标移动相机的观察点? 的相关文章

  • 从iOS iphone中相机返回的图像中读取GPS数据

    我需要获取使用 iOS 设备相机拍摄的图像的 GPS 坐标 我不关心相机胶卷图像 只关心使用 UIImagePickerController SourceType Camera 拍摄的图像 我读过很多 stackoverflow 答案 比如
  • 获取光标相对于控件的位置 - C#

    我想获取鼠标相对于鼠标指针所在控件的位置 这意味着当我将光标置于控件的起点 左上角 时 它应该给出 0 0 我正在使用以下代码 private void panel1 MouseMove object sender MouseEventAr
  • 如何在OpenGL中像这样绘制连接的带状线

    我想用以下方式绘制一系列连接线 GL LINE STRIP 我尝试过自己编写代码 但没有得到想要的结果 所以我来到这里 帮助我找出我错在哪里 这里我只给出我的draw 函数 glBegin GL LINE STRIP glVertex2f
  • 带有 std::vector 的 VBO

    我用 C 和 OpenGL 编写了一个模型加载器 我用过std vectors 来存储我的顶点数据 但现在我想将其传递给glBufferData 但是数据类型却截然不同 我想知道是否有办法可以相互转换std vector至已记录的const
  • 使用普通画布/文本输出更新LayeredWindow

    有没有一种方法可以使用画布在表单上绘图 然后使用 updatelayeredwindow 这样表单就不可见 但文本可见 就像只显示文本的半透明表单一样 如果没有 那么有没有办法只用画布 opengl directx 制作某种半透明形式 我想
  • 识别鼠标移动的算法

    我想知道是否有任何研究 算法可以指定鼠标在识别 等字符时的偏差量使用鼠标绘制 某种光学字符识别 但可能是一个更简单的版本 是否有某种算法可以让我说用户绘制的问号确实是一个问号 而不是其他具有一定准确性的东西 就像 Windows 平板电脑软
  • iOS 7 从 Mobile Safari 访问 iPhone 相机?

    In iOS 6你可以使用这样的东西
  • Motorola Android 2.2 相机忽略 EXTRA_OUTPUT 参数

    我以编程方式打开相机来拍摄视频 我告诉相机使用如下代码将视频文件放置到指定位置 Intent intent new Intent MediaStore ACTION VIDEO CAPTURE File out new File sdcar
  • nVidia 和 ATI 之间的 OpenGL 渲染差异

    最近 我将 ATI 驱动程序 我使用的是 HD7970 更新为最新版本 但我的 OpenGL 项目的一些对象停止工作 更重要的是 他们适用于 nVidia 最新驱动程序 在 960m 上测试 ATI 和 nVidia 渲染管道之间有什么我应
  • SDL 程序中颜色关闭

    我目前正在开发一个非常简单的游戏 使用纯 C 方法和 SDL 及其官方额外库 如 SDL image 和 OpenGL 现在 虽然我遇到了一些障碍 但我不知道为什么要这样做 绘制时颜色全部关闭 我目前在 Mac 上运行该程序 但如果我没记错
  • 如何在 unity3d C# 中让相机跟随物体?

    我有一个名为 Ball 的对象 我向它添加了键盘交互性 WASD 来移动球 我需要摄像机留在后面并跟随球 但我遇到了错误 using UnityEngine using System Collections public class bal
  • 如何使用OpenGL数组纹理?

    我正在尝试在OpenGL中使用精灵表 通过数组纹理实现它这就是我加载纹理的方式 QImage image image load C QtProjects project images spritesheet png png const un
  • Android webview 允许使用当前 Android Studio 访问相机

    我一直在尝试在我的网络视图上制作一个简单的输入按钮来访问相机和文件 我读过的解决方案各不相同 只有几行代码 没有解释它们到我在 Android Studio 中下载并打开的盲 github 项目链接 Os Fileup 等 的位置 并且它们
  • OpenGL 新手: glutMouseFunc

    我试图在单击鼠标后更改球体位置 但在使用 glutMouseFunc 中的 x 和 y 时它不起作用 以下是代码 include stdafx h include
  • 3D 图形批处理

    很多网站 文章都说 批量 批 批 有人可以解释一下着色器中的 批处理 代表什么吗 即 是否 改变纹理 更改任意着色器变量 意味着某些东西不能 批处理 最简单的总结方法就是尝试尽可能少地调用 API 来绘制您需要绘制的内容 使用顶点数组或 V
  • Camera.open()返回NULL Android开发

    我正在按照构建相机应用程序的教程进行操作http developer android com tools device html http developer android com tools device html我对 Camera o
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前
  • 如何在 Android 中保存相机的临时照片?

    在尝试从相机拍照并将其保存到应用程序的缓存文件夹中时 我没有得到任何可见的结果 应用程序不会崩溃 但在 LogCat 上 当我尝试将 ImageView src 字段设置为刚刚获取的文件的 URI 时 我收到此消息 09 17 14 03
  • Libgdx SpriteBatch.draw() 指定 4 个顶点

    我正在使用 libGdx 创建一个 2d 游戏 并尝试使用这个特殊的方法来绘制一个简单的 2d 纹理 分别指定 4 个顶点 draw Texture texture float spriteVertices int offset int l
  • 自定义相机视图 Swift iOS 8 iPhone Xcode 6.1

    我想在 iPhone 的 View 中使用相机 我不想使用典型的全屏相机视图 而是我自己的 例如 我想在屏幕中间有一个 200x200 的正方形 并且有一个相机预览 在这个方块下面我想要一个拍照按钮 怎么做 我是新手 速度很快 你会想要使用

随机推荐