我正在尝试实现一个圆球风格的相机。我使用glm::look来保持相机指向目标,然后使用方位角/倾斜角在球体表面周围移动它来旋转视图。
我遇到了一个问题,当方位角接近90度时,视图会翻转过来。
相关代码如下:
-
获取投影和视图矩阵。在主循环中运行
void Visual::updateModelViewProjection() { model = glm::mat4(); projection = glm::mat4(); view = glm::mat4(); projection = glm::perspective ( (float)glm::radians(camera.Zoom), (float)width / height, // aspect ratio 0.1f, // near clipping plane 10000.0f // far clipping plane ); view = glm::lookAt(camera.Position, camera.Target, camera.Up); }
-
鼠标移动事件,用于相机旋转
void Visual::cursor_position_callback(GLFWwindow* window, double xpos, double ypos) { if (leftMousePressed) { ... } if (rightMousePressed) { GLfloat xoffset = (xpos - cursorPrevX) / 4.0; GLfloat yoffset = (cursorPrevY - ypos) / 4.0; camera.inclination += yoffset; camera.azimuth -= xoffset; if (camera.inclination > 89.0f) camera.inclination = 89.0f; if (camera.inclination < 1.0f) camera.inclination = 1.0f; if (camera.azimuth > 359.0f) camera.azimuth = 359.0f; if (camera.azimuth < 1.0f) camera.azimuth = 1.0f; float radius = glm::distance(camera.Position, camera.Target); camera.Position[0] = camera.Target[0] + radius * cos(glm::radians(camera.azimuth)) * sin(glm::radians(camera.inclination)); camera.Position[1] = camera.Target[1] + radius * sin(glm::radians(camera.azimuth)) * sin(glm::radians(camera.inclination)); camera.Position[2] = camera.Target[2] + radius * cos(glm::radians(camera.inclination)); camera.updateCameraVectors(); } cursorPrevX = xpos; cursorPrevY = ypos; }
-
计算相机方向矢量
void updateCameraVectors() { Front = glm::normalize(Target-Position); Right = glm::rotate(glm::normalize(glm::cross(Front, {0.0, 1.0, 0.0})), glm::radians(90.0f), Front); Up = glm::normalize(glm::cross(Front, Right)); }
我很确定这与我计算相机右矢量的方式有关,但我不知道如何补偿。
有人遇到过这种情况吗?有什么建议吗?
使用lookAt
旋转相机是一个常见的错误。你不应该。向后/右/向上方向是视图矩阵的列。如果你已经有了它们,那么你甚至不需要lookAt
,它会尝试重做一些计算。另一方面,lookAt
一开始并不能帮助你找到那些向量。
首先将视图矩阵构建为平移和旋转的组合,然后从其列中提取这些向量:
void Visual::cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
...
if (rightMousePressed)
{
GLfloat xoffset = (xpos - cursorPrevX) / 4.0;
GLfloat yoffset = (cursorPrevY - ypos) / 4.0;
camera.inclination = std::clamp(camera.inclination + yoffset, -90.f, 90.f);
camera.azimuth = fmodf(camera.azimuth + xoffset, 360.f);
view = glm::mat4();
view = glm::translate(view, glm::vec3(0.f, 0.f, camera.radius)); // add camera.radius to control the distance-from-target
view = glm::rotate(view, glm::radians(camera.inclination + 90.f), glm::vec3(1.f,0.f,0.f));
view = glm::rotate(view, glm::radians(camera.azimuth), glm::vec3(0.f,0.f,1.f));
view = glm::translate(view, camera.Target);
camera.Right = glm::column(view, 0);
camera.Up = glm::column(view, 1);
camera.Front = -glm::column(view, 2); // minus because OpenGL camera looks towards negative Z.
camera.Position = glm::column(view, 3);
view = glm::inverse(view);
}
...
}
然后从updateModelViewProjection
和updateCameraVectors
中删除计算视图和方向向量的代码。
免责声明:此代码未经测试。您可能需要修复某个地方的负号,操作顺序,或者约定可能不匹配(Z是上或Y是上,等等…)。