3d 相机有意外滚动
3d camera has unintended roll
我一直在使用 C++ 在 opengl 中开发 3d 相机。
当我拿着相机环顾四周时,有时相机会出现意外的滚动,尤其是当我在旋转相机时。
我怀疑这是一个浮点错误,但我不知道如何检测它。
这是相机class:
#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
该代码有两个问题:
首先,如果 xOffset
、yOffset
只是屏幕像素差异(通过鼠标位置获得),您必须设置一个将它们转换为 角度.有更好的方法,例如从 window 的中心到鼠标位置(先前和当前)形成两个向量,并通过点积计算它们之间的角度。根据 glm 设置(默认为度数,但您可以设置弧度),non-factorized xOffset 可能是一个巨大的角度,而不是平滑的旋转。
newViewMatrix = thisMouseRotation * oldViewMatrix
的第二次累积旋转会在一些移动后退化矩阵。 这是由于计算机的数量有限:例如10/3=3.333 但 3.333*3=9.999 != 10
解决方案:
- A) 将旋转存储在四元数中。初始化一个四元数和
每次旋转更新它
newQuater = thisMoveQuater * oldQuater
。
时时"normalize"四元数求最小
号码问题。
viewMatrix 是通过viewMatrix = Mat4x4FromQuaternion * translationMatrix
计算的所以我们避免了前面的
viewMatrix 及其问题。
- B) 累积围绕 每个 X、Y、Z 轴的旋转角度。计算
每次需要使用这些累积的旋转矩阵
角度。也许您将角度值限制在 0.2 之类的值
度。这样,用户可以获得与多个用户相同的位置
旋转之前。
我设法弄明白了。尽管我必须完全重写它才能做到这一点。
我的问题出在这几行:
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;
通过在最后一行乘以方向,不会发生意外滚动。
我一直在使用 C++ 在 opengl 中开发 3d 相机。
当我拿着相机环顾四周时,有时相机会出现意外的滚动,尤其是当我在旋转相机时。
我怀疑这是一个浮点错误,但我不知道如何检测它。
这是相机class:
#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
该代码有两个问题:
首先,如果 xOffset
、yOffset
只是屏幕像素差异(通过鼠标位置获得),您必须设置一个将它们转换为 角度.有更好的方法,例如从 window 的中心到鼠标位置(先前和当前)形成两个向量,并通过点积计算它们之间的角度。根据 glm 设置(默认为度数,但您可以设置弧度),non-factorized xOffset 可能是一个巨大的角度,而不是平滑的旋转。
newViewMatrix = thisMouseRotation * oldViewMatrix
的第二次累积旋转会在一些移动后退化矩阵。 这是由于计算机的数量有限:例如10/3=3.333 但 3.333*3=9.999 != 10
解决方案:
- A) 将旋转存储在四元数中。初始化一个四元数和
每次旋转更新它
newQuater = thisMoveQuater * oldQuater
。
时时"normalize"四元数求最小 号码问题。
viewMatrix 是通过viewMatrix = Mat4x4FromQuaternion * translationMatrix
计算的所以我们避免了前面的 viewMatrix 及其问题。 - B) 累积围绕 每个 X、Y、Z 轴的旋转角度。计算 每次需要使用这些累积的旋转矩阵 角度。也许您将角度值限制在 0.2 之类的值 度。这样,用户可以获得与多个用户相同的位置 旋转之前。
我设法弄明白了。尽管我必须完全重写它才能做到这一点。
我的问题出在这几行:
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;
通过在最后一行乘以方向,不会发生意外滚动。