#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