OpenGL 浮点精度产生意想不到的差异

OpenGL float precision makes unexpected differece

我有一个功能,可以通过偏航角和俯仰角围绕玩家旋转相机。

void Camera::updateVectors() {
    GLfloat radius = glm::length(center - position);
    position.x = cos(glm::radians(this->yaw)) * cos(glm::radians(this->pitch));
    position.y = sin(glm::radians(this->pitch));
    position.z = sin(glm::radians(this->yaw)) * cos(glm::radians(this->pitch));
    position *= radius;
    this->front = glm::normalize(center - position);
    this->right = glm::normalize(glm::cross(this->front, this->worldUp));
    this->up = glm::normalize(glm::cross(this->right, this->front));
    lookAt = glm::lookAt(this->position, this->position + this->front, this->up);
}

当我移动玩家时,通过向相机的中心和位置添加平移矢量,相机应该随之移动:

void Camera::Transform(glm::vec3& t) {
    this->position += t;
    this->center += t;
}

在移动玩家之前,摄像机旋转工作正常,玩家移动也正常,但是一旦我尝试在玩家移动后旋转摄像机,它开始意外地改变位置。

经过一些调试后,我注意到在第一行计算的半径是中心和相机位置之间的距离,如 49.888889 或 50.000079,由于初始化值,它应该是 50.0,这个差异很小让结果完全出乎意料。 那么我该如何处理这个浮点精度或者我的代码或计算中是否存在错误。

编辑: 玩家的位置取决于它的偏航和俯仰并更新相机的中心

GLfloat velocity = this->movementSpeed * deltaTime;
if (direction == FORWARD) {
    glm::vec3 t = glm::vec3(sin(glm::radians(yaw)), sin(glm::radians(pitch)), cos(glm::radians(yaw))) * velocity;
    matrix = glm::translate(matrix, t);
    for (GLuint i = 0; i < this->m_Entries.size(); i++) {
        this->m_Entries[i].setModelMatrix(matrix);
    }
    glm::vec3 f(matrix[2][0], matrix[2][1], matrix[2][2]);
    f *= velocity;
    scene->getDefCamera()->Transform(f);
}
if (direction == BACKWARD) {
    glm::vec3 t = glm::vec3(sin(glm::radians(yaw)), 0.0, cos(glm::radians(yaw))) * velocity;
    matrix = glm::translate(matrix, -t);
    for (GLuint i = 0; i < this->m_Entries.size(); i++) {
        this->m_Entries[i].setModelMatrix(matrix);
    }
    glm::vec3 f(matrix[2][0], matrix[2][1], matrix[2][2]);
    f *= velocity;
    f = -f;
    scene->getDefCamera()->Transform(f);
}

这里的主要问题是您根据移动的位置进行旋转。但是旋转是基于坐标系的原点。所以当你移动位置时,旋转仍然是相对于原点进行的。

与其让 Transform 偏移 position,不如只偏移 center。确实,存储 position 没有任何意义;您计算 相机的位置基于其当前的中心点、半径和旋转角度。半径是一个 属性,应该存储,而不是计算。

解决方案只是在相机视图矩阵上进行转换,而不是通过 lookAt 函数进行转换 首先初始化相机

void Camera::initCamera(glm::vec3& pos, glm::vec3& center, GLfloat yaw, GLfloat pitch) {
    view = glm::translate(view, center-pos);
    view = glm::rotate(view, glm::radians(yaw), glm::vec3(0.0, 1.0, 0.0));
    view = glm::rotate(view, glm::radians(pitch), glm::vec3(1.0, 0.0, 0.0));
    view = glm::translate(view, pos-center);
}

然后旋转函数:

void Camera::Rotate(GLfloat xoffset, GLfloat yoffset, glm::vec3& c) {
    xoffset *= this->mouseSensitivity;
    yoffset *= this->mouseSensitivity;
    view = glm::translate(view, c );//c is the player position
    view = glm::rotate(view, glm::radians(xoffset), glm::vec3(0.0, 1.0, 0.0));
    view = glm::rotate(view, glm::radians(yoffset), glm::vec3(1.0, 0.0, 0.0));
    view = glm::translate(view,  - c);
}

和相机移动功能:

void Camera::Translate(glm::vec3& t) {
    view = glm::translate(view, -t);
}

并且在播放器中 class 当播放器移动时,它会通过此代码

推动相机朝其方向移动
void Mesh::Move(Move_Directions direction, GLfloat deltaTime) {
GLfloat velocity = 50.0f * this->movementSpeed * deltaTime;
if (direction == FORWARD) {
    glm::vec3 t = glm::vec3(sin(glm::radians(yaw)), sin(glm::radians(pitch)), cos(glm::radians(yaw))) * velocity;
    matrix = glm::translate(matrix, t);
    for (GLuint i = 0; i < this->m_Entries.size(); i++) {
        this->m_Entries[i].setModelMatrix(matrix);
    }
    glm::vec3 f(matrix[2][0], matrix[2][1], matrix[2][2]);
    f *= velocity;
    scene->getDefCamera()->Translate(f);
}
if (direction == BACKWARD) {
    glm::vec3 t = glm::vec3(sin(glm::radians(yaw)), 0.0, cos(glm::radians(yaw))) * velocity;
    matrix = glm::translate(matrix, -t);
    for (GLuint i = 0; i < this->m_Entries.size(); i++) {
        this->m_Entries[i].setModelMatrix(matrix);
    }
    glm::vec3 f(matrix[2][0], matrix[2][1], matrix[2][2]);
    f *= velocity;
    f = -f;
    scene->getDefCamera()->Translate(f);
}
if (direction == RIGHT) {
    matrix = glm::rotate(matrix, (GLfloat) -M_PI * deltaTime, glm::vec3(0.0, 1.0, 0.0));
    for (GLuint i = 0; i < this->m_Entries.size(); i++) {
        this->m_Entries[i].setModelMatrix(matrix);
    }
}
if (direction == LEFT) {
    matrix = glm::rotate(matrix, (GLfloat) M_PI * deltaTime, glm::vec3(0.0, 1.0, 0.0));
    for (GLuint i = 0; i < this->m_Entries.size(); i++) {
        this->m_Entries[i].setModelMatrix(matrix);
    }
}
}

感谢大家的帮助