Невозможно переместить матрицу представления и направление, на которое указывает матрица представления одновременно

#c #math #opengl #glfw #glm-math

#c #математика #opengl #glfw #glm-math

Вопрос:

Когда я перемещаюсь по сцене (не двигая «головой», на которую указывает моя матрица вида), все работает просто отлично. Когда я смотрю вокруг, не двигаясь, все работает просто отлично. Но когда я объединяю их, моя голова (матрица представления) начинает сходить с ума. Я предполагаю, что проблема в моей математике / логике, но я просто не могу понять это.

Вот мой код (упрощенный, конечно):

 #define MOVE_DISTANCE 0.15f
#define LOOK_SENSITIVITY 0.01f

static GLFWwindow* window;

static glm::mat4 viewMatrix;
static glm::vec3 look_at;


static void mouseCallback(GLFWwindow* window, double x, double y){
  static double prev_x = 0;
  static double prev_y = 0;
  static float pitch = 0, yaw = 0;

  pitch  = (y-prev_y) * LOOK_SENSITIVITY;
  yaw  = (x-prev_x) * LOOK_SENSITIVITY;

  if(pitch > 89){
    pitch = 89;
  }
    if(pitch < -89){
      pitch = -89;
  }

  look_at = glm::vec3(cos(yaw)*cos(pitch), sin(pitch), sin(yaw)*cos(pitch) - 1.0f);

  prev_x = x;
  prev_y = y;
}

int main(){
  window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL); //I left the rest of the initialization out

  glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  glfwSetCursorPosCallback(window, mouseCallback);

  look_at = glm::vec3(0.0f, 0.0f, -1.0f);
  glm::vec3 position(0.0f, 0.0f, 0.0f);

  glm::mat4 projectionMatrix = glm::perspective(45.0f, (float) 800/600, 0.1f, 100.0f); //fov, screen aspect ratio, near render distance, far render distance (z axis)
  glm::mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.0f));
  viewMatrix = glm::lookAt(position, look_at, glm::vec3(0, 1, 0));


  while(!glfwWindowShouldClose(window)){
    if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS){
      position.z -= MOVE_DISTANCE;
      look_at.z -= MOVE_DISTANCE;
    }
    if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS){
      position.z  = MOVE_DISTANCE;
      look_at.z  = MOVE_DISTANCE;
    }
    if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS){
      position.x  = MOVE_DISTANCE;
      look_at.x  = MOVE_DISTANCE;
    }
    if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS){
      position.x -= MOVE_DISTANCE;
      look_at.x -= MOVE_DISTANCE;
    }
    if(glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS){ //FLy up
      position.y  = MOVE_DISTANCE;
      look_at.y  = MOVE_DISTANCE;
    }
    if(glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS){ //Fly down
      position.y -= MOVE_DISTANCE;
      look_at.y -= MOVE_DISTANCE;
    }

    viewMatrix = glm::lookAt(position, look_at position, glm::vec3(0, 1, 0));

    glClear(GL_COLOR_BUFFER_BIT);

    glDrawElements(GL_TRIANGLES, NUM_OF_INDICES, GL_UNSIGNED_BYTE, 0);

    glfwSwapBuffers(window);
    glfwPollEvents();
  }
}
 

Всякий раз, когда я перемещаюсь при перемещении мыши, ситуация выходит из-под контроля, и моя голова (направление, в котором указывает моя матрица вида) начинает вращаться. Я знаю, что направление, в котором я смотрю, не влияет на движение. Я планирую реализовать это когда-нибудь в будущем.

Может кто-нибудь помочь мне указать на мою ошибку?

Комментарии:

1. Для добавления вам нужен вектор «вперед» position , который указывает в направлении, в котором вы хотите посмотреть. Или, поскольку вы обновляете look_at , не добавляйте его position . Просто используйте look_at само по себе.

Ответ №1:

Ваша проблема в том, что независимо от того, куда вы смотрите, нажатие W всегда будет вычитаться MOVE_DISTANCE из вашей position.z координаты мирового пространства, даже если перемещение «вперед» или «назад» не обязательно означает «перемещение вдоль мировой оси Z».

Итак, вы должны определить оси «вперед» (для перемещения вперед и назад), а также «вправо» (или «влево») и «вверх» или «вниз». Последнее может и не понадобиться, если вы хотите, чтобы ПРОБЕЛ и CTRL всегда перемещались вверх / вниз по мировой оси Y.

Для двух других осей, когда у вас есть любая произвольная матрица вида m (например, ваша матрица, построенная с помощью glm::lookAt ), вы можете легко извлечь right up forward из нее векторы и, например, так:

 #include <glm/gtc/matrix_access.hpp>
...
glm::vec4 right(glm::row(m, 0));
glm::vec4 up(glm::row(m, 1));
glm::vec4 forward(-glm::row(m, 2));
 

Как сказал @3Dave выше в комментарии, вы уже знаете, где находится «вперед», потому что вы сами манипулируете этим вектором и вычисляете из него преобразование вида, поэтому вы могли бы это опустить.

Теперь, когда вы хотите перемещаться влево / вправо, вы вычитаете / добавляете right в свое мировое пространство position , а также для других осей.

Еще одна неправильная вещь заключается в том, что вы также добавляете / вычитаете из своего look_at направления при перемещении с помощью клавиш клавиатуры. Это неправильно, потому что ваш look_at вектор является направлением, и вы уже используете его как вектор направления при вычислении аргументов для glm::lookAt вызова.

Включение всего этого в ваш код (вместе с некоторыми небольшими рефакторингами) приведет к следующему циклу while:

 while (!glfwWindowShouldClose(window)) {
  glm::vec3 right(glm::row(viewMatrix, 0)),
            up(glm::row(viewMatrix, 1)),
            forward(-glm::row(viewMatrix, 2));
  glm::vec3 v(0, 0, 0);
  if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    v  = forward;
  if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
    v -= forward;
  if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
    v  = right;
  if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
    v -= right;
  position  = v * MOVE_DISTANCE;
  viewMatrix = glm::lookAt(position, position   look_at, glm::vec3(0, 1, 0));
}
 

Комментарии:

1. Итак, не могли бы вы исправить предоставленный мной код и включить его в ответ, чтобы помочь мне лучше понять, что вы имеете в виду?

2. При компиляции я получаю следующую ошибку: ошибка: ‘row’ не является членом ‘glm’; вы имели в виду ‘pow’?

3. о, извините, я включил matrix_transform, а не matrix_access