c++ 带有自定义旋转的glm::lookAt无法正常工作

camsedfj  于 2023-02-01  发布在  其他
关注(0)|答案(1)|浏览(166)

我希望控制相机,使其可以围绕模型旋转。
理论代码应为:

// `camera_rotation_angle_x_` and `camera_rotation_angle_y_` are initialized to 0, and can be modified by the user.

glm::mat4 CreateViewMatrix(glm::vec3 eye_pos, glm::vec3 scene_center, glm::vec3 up_vec) {
  auto eye_transform = glm::translate(glm::mat4(1.0f), -scene_center); // recenter
  eye_transform = glm::rotate(eye_transform, camera_rotation_angle_x_, glm::vec3(1.0f, 0.0f, 0.0f));
  eye_transform = glm::rotate(eye_transform, camera_rotation_angle_y_, glm::vec3(0.0f, 1.0f, 0.0f));
  eye_transform = glm::translate(eye_transform, scene_center); // move back
  eye_pos = eye_transform * glm::vec4(eye_pos, 1.0f);
  up_vec = eye_transform * glm::vec4(up_vec, 0.0f);
  return glm::lookAt(eye_pos, scene_center, up_vec);
}

但是“重新居中”和“移回”这两行必须写为如下才能正确旋转,否则当旋转参数改变时,从相机到中心的距离会发生变化:

auto eye_transform = glm::translate(glm::mat4(1.0f), scene_center); // recenter *The sign has changed*
...
eye_transform = glm::translate(eye_transform, -scene_center); // move back *The sign has changed*
...
// Correctness test, only when the distance remains constant, the rotation logic is correct.
cout << "eye_pos: " << eye_pos[0] << ", " << eye_pos[1] << ", " << eye_pos[2] << endl;
cout << "distance: " << (sqrt(
    pow(eye_pos[0] - scene_center[0], 2)
        + pow(eye_pos[1] - scene_center[1], 2)
        + pow(eye_pos[2] - scene_center[2], 2)
)) << endl;

先减去中心值再加回来才是正确的逻辑,先加后减没有任何意义。
那么,到底是什么原因导致我必须编写带有逻辑错误的代码才能使其正常工作呢?
调用方的代码如下,可能bug就在这里?

// `kEyePos`, `kSceneCenter`, `kUpVec`, `kFovY`, `kAspect`, `kDistanceEyeToBack` and `kLightPos` are constants throughout the lifetime of the program

UniformBufferObject ubo{};
ubo.model = glm::mat4(1.0f);
ubo.view = CreateViewMatrix(kEyePos, kSceneCenter, kUpVec);
ubo.proj = glm::perspective(kFovY, kAspect, 0.1f, kDistanceEyeToBack);
// GLM was originally designed for OpenGL, where the Y coordinate of the clip coordinates is inverted.
// The easiest way to compensate for that is to flip the sign on the scaling factor of the Y axis in the projection matrix.
// Because of the Y-flip we did in the projection matrix, the vertices are now being drawn in counter-clockwise order instead of clockwise order.
// This causes backface culling to kick in and prevents any geometry from being drawn.
// You should modify the frontFace in `VkPipelineRasterizationStateCreateInfo` to `VK_FRONT_FACE_COUNTER_CLOCKWISE` to correct this.
ubo.proj[1][1] *= -1;
ubo.light_pos = glm::vec4(kLightPos, 1.0f); // The w component of point is 1

memcpy(vk_buffer_->GetUniformBufferMapped(frame_index), &ubo, sizeof(ubo));
f4t66c6m

f4t66c6m1#

我发现问题出在矩阵构造的顺序上,而不是glm::lookAt方法。

m = glm::rotate(m, angle, up_vec)

相当于

m = m * glm::rotate(glm::mat4(1), angle, up_vec)

,不

m = glm::rotate(glm::mat4(1), angle, up_vec) * m

正如我所料。
这很容易解释为什么交换“重新居中”和“移回”工作正常。
正确的代码如下所示:

glm::mat4 VulkanRendering::CreateViewMatrix(glm::vec3 eye_pos, glm::vec3 scene_center, glm::vec3 up_vec) const {
  // Create transform matrix in reverse order.
  // First rotate around the X axis, and then around the Y axis, otherwise it does not match the practice of most games.
  auto view_transform = glm::translate(glm::mat4(1.0f), scene_center); // last: move back
  view_transform = glm::rotate(view_transform, camera_rotation_angle_y_, glm::vec3(0.0f, 1.0f, 0.0f));
  view_transform = glm::rotate(view_transform, camera_rotation_angle_x_, glm::vec3(1.0f, 0.0f, 0.0f));
  view_transform = glm::translate(view_transform, -scene_center); // first: recenter

  eye_pos = view_transform * glm::vec4(eye_pos, 1.0f); // The w component of point is 1
  up_vec = view_transform * glm::vec4(up_vec, 0.0f); // The w component of vector is 0
  return glm::lookAt(eye_pos, scene_center, up_vec);
}

相关问题