3D相机具有意外的卷

3d camera has unintended roll

本文关键字:意外 相机 3D      更新时间:2023-10-16

我一直在使用C 的OpenGL中使用3D摄像头。

当我用相机环顾四周时,有时相机中会出现意外的滚动,尤其是当我旋转相机时。

我怀疑这是浮点错误,但我不知道如何检测。

这是相机类:

#ifndef CAMERA_H
#define CAMERA_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/string_cast.hpp>
#include <iostream>
using glm::vec3;
using glm::mat4;
using glm::quat;
enum CamDirection {
    CAM_FORWARD,
    CAM_BACKWARD,
    CAM_LEFT,
    CAM_RIGHT
};

class Camera {
public:
    void cameraUpdate();
    mat4 getViewMatrix();
    Camera();
    Camera(vec3 startPosition);
    void move(CamDirection dir, GLfloat deltaTime);
    void look(double xOffset, double yOffset);
    void update();
private:
    mat4 viewMatrix;
    const GLfloat camSpeed = 5.05f;
};
mat4 Camera::getViewMatrix() {
    return viewMatrix;
}
Camera::Camera(){}

Camera::Camera(vec3 startPos):
    viewMatrix(glm::lookAt(startPos, vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f)))
{}
void Camera::move(CamDirection dir, GLfloat deltaTime) {
    mat4 trans;
    const vec3 camForward = vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]);
    const vec3 camRight   = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
    if (dir == CAM_FORWARD)
        trans = glm::translate(trans,      (camSpeed * deltaTime) * camForward);
    else if (dir == CAM_BACKWARD)
        trans = glm::translate(trans, -1 * (camSpeed * deltaTime) * camForward);
    else if (dir == CAM_RIGHT)
        trans = glm::translate(trans, -1 * (camSpeed * deltaTime) * camRight);
    else
        trans = glm::translate(trans,      (camSpeed * deltaTime) * camRight);
    viewMatrix *= trans;
}
void Camera::look(double xOffset, double yOffset) {
    // 2 * acos(q[3])
    quat rotation = glm::angleAxis((GLfloat)xOffset, vec3( 0.0f, 1.0f, 0.0f));
    viewMatrix = glm::mat4_cast(rotation) * viewMatrix;
    rotation = glm::angleAxis((GLfloat)yOffset, vec3(-1.0f, 0.0f, 0.0f));
    mat4 rotMatrix = glm::mat4_cast(rotation);
    viewMatrix = rotMatrix * viewMatrix;
}
void Camera::update() {
}
#endif // CAMERA_H

我设法弄清楚了。尽管我必须完全重写它才能做到这一点。

我的问题是在这些行上:

quat rotation = glm::angleAxis((GLfloat)xOffset, vec3( 0.0f, 1.0f, 0.0f));
viewMatrix = glm::mat4_cast(rotation) * viewMatrix;
rotation = glm::angleAxis((GLfloat)yOffset, vec3(-1.0f, 0.0f, 0.0f));
mat4 rotMatrix = glm::mat4_cast(rotation);

构建一个中间四基因以存储方向而使用,我可以用以下方式替换look方法:

quat pitch = quat(vec3(-yOffset, 0.0f, 0.0f));
quat yaw = quat(vec3(0.f, xOffset, 0.f));
orientation = pitch * orientation * yaw;

通过将方向乘以最后一行的方式,就不会发生意外滚动。

该代码中有两个问题:

首先,如果xOffsetyOffset只是屏幕像素差异(通过鼠标位置获得),则必须设置一个将它们转化为 Angles 的因素。有更好的方法,例如,从窗口中心到鼠标位置(以前和电流)形成两个向量,并通过点乘积计算它们之间的角度。取决于GLM集(默认程度,但可以设置弧度),一个非因素的Xoffset可能是一个巨大的角度,而不是平滑的旋转。

第二次通过newViewMatrix = thisMouseRotation * oldViewMatrix累积旋转,在某些动作后将矩阵归化。这是由于计算机的数字表示有限:例如10/3 = 3.333但是3.333*3 = 9.999!= 10

解决方案:

  • a)将旋转存储在四元中。初始化四元组和每次旋转newQuater = thisMoveQuater * oldQuater更新它。
    时间将四个季度"正常化",以最大程度地减少数字问题。
    ViewMatrix由viewMatrix = Mat4x4FromQuaternion * translationMatrix计算ViewMatrix及其问题。
  • b)在 x,y,z轴周围累积旋转角度。计算每次需要使用这些累积的旋转矩阵角度。也许您将角度值夹至0.2学位。这样,用户可以达到与多个相同的位置旋转之前。