繁体   English   中英

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

[英]glm::lookAt with custom rotation does not work properly

我想控制我的相机,让它可以围绕 model 旋转。

理论代码应该是:

// `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);
}

但是“recenter”和“move back”这两行必须写成如下才能正确旋转,否则旋转参数改变时相机到中心的距离会发生变化:

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;

先减去中心值再加回来才是正确的逻辑。 先加后减没有任何意义。
那么我必须编写带有逻辑错误的代码才能使其正常工作,这是怎么回事?

调用者的代码在下面,也许错误在这里?

// `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));

我发现问题出在矩阵构造的顺序上,而不是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

和我想的一样。

这很容易解释为什么交换“recenter”和“move back”可以正常工作。

正确的代码如下:

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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM