【OpenGL学习】Camera 类的抽象
创始人
2024-05-20 02:49:36
0

Camera 类的抽象

本节中,我们希望将 Camera 单独抽象到一个类当中,便于我们使用,camera 类需要做的事有:保存相机的三维空间信息,计算相机空间坐标向量,计算并存储 view 和 projection 矩阵,以及处理各种输入。

首先,我在成员变量中添加了以下变量:

private:glm::vec3 m_position;glm::vec3 m_world_up;glm::vec3 m_front;glm::vec3 m_up;glm::vec3 m_right;glm::vec3 m_euler;float m_fov;float m_camera_speed;float m_mouse_sensitivity;float m_zoom;glm::mat4 m_view_matrix;glm::mat4 m_view_projection_matrix;

分别用于保存相机的位置信息,up_vector,以及相机坐标系对应的三个向量,还有用于控制相机旋转的欧拉角,同时还加入了相机的视场角 fov ,控制相机移速的变量,控制鼠标灵敏度的变量和控制缩放的系数,最后添加了 view 矩阵和 view-projection 矩阵,用于保存变换矩阵。

首先思考通常情况下初始化相机用到的参数:一般有相机位置,视点看向的位置,up-vector,相机的视场角和表示相机当前旋转的欧拉角:

Perspective_Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 at = glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float fov = 45.0f, glm::vec3 euler = glm::vec3(0.0f, -90.0f, 0.0f));

然后声明更新相机空间基向量和更新 view 矩阵的两个函数:

	void update_view_matrix();void update_camera_space_vector();

这两个函数用于初始化和当我们更新了某些参数的时候,对 view 矩阵和基向量也进行更新。

有时候可能需要获取相机保存的矩阵和欧拉角供我们使用,所以这里提供了如下接口:

	const glm::mat4 get_view_matrix() const { return m_view_matrix; }const glm::mat4 get_view_projection_matrix() const { return m_view_projection_matrix; }const glm::vec3 get_euler() const { return m_euler; }

用于控制相机移动速度和鼠标灵敏度的参数调节:

	void set_camera_speed(float speed) { m_camera_speed = speed; }void set_mouse_sensitivity(float sensitivity) { m_mouse_sensitivity = sensitivity; }

最后创建用于处理输入的函数:

	void process_key_event(camera_movement direction, float delta_time);void process_mouse_event(float x_offset, float y_offset, GLboolean constrainPitch = true);void process_scroll_event(float y_offset);

在构造函数中,首先完成我们定义的成员变量的初始化:

Perspective_Camera::Perspective_Camera(glm::vec3 position, glm::vec3 at, glm::vec3 up, float fov, glm::vec3 euler):m_position(position), m_world_up(up), m_fov(fov), m_euler(euler), m_camera_speed(2.5f), m_mouse_sensitivity(0.005f)
{m_zoom = m_fov;update_camera_space_vector();update_view_matrix();
}

更新空间坐标向量的函数定义如下:

void Perspective_Camera::update_camera_space_vector()
{glm::vec3 front;front.x = cos(glm::radians(m_euler.x)) * cos(glm::radians(m_euler.y));front.y = sin(glm::radians(m_euler.x));front.z = cos(glm::radians(m_euler.x)) * sin(glm::radians(m_euler.y));m_front = glm::normalize(front);m_right = glm::normalize(glm::cross(m_front, m_world_up));m_up = glm::normalize(glm::cross(m_right, m_front));
}

更新两个矩阵的函数定义如下:

void Perspective_Camera::update_view_matrix()
{m_view_matrix = glm::lookAt(m_position, m_position + m_front, m_up);glm::mat4 projection_matrix = glm::perspective(glm::radians(m_fov), 800.0f / 600.0f, 0.1f, 100.0f);m_view_projection_matrix = projection_matrix * m_view_matrix;
}

处理用户输入的三个函数分别如下:

enum camera_movement {FORWARD, BACKWARD, LEFT, RIGHT
};
void Perspective_Camera::process_key_event(camera_movement direction, float delta_time)
{float speed = m_camera_speed * delta_time;if (direction == FORWARD)m_position += m_front * speed;if (direction == BACKWARD)m_position -= m_front * speed;if (direction == LEFT)m_position -= m_right * speed;if (direction == RIGHT)m_position += m_right * speed;update_view_matrix();
}
void Perspective_Camera::process_mouse_event(float x_offset, float y_offset, GLboolean constrain_pitch)
{x_offset *= m_mouse_sensitivity;y_offset *= m_mouse_sensitivity;m_euler.y += x_offset;m_euler.x += y_offset;// make sure that when pitch is out of bounds, screen doesn't get flippedif (constrain_pitch){if (m_euler.x > 89.0f)m_euler.x = 89.0f;if (m_euler.x < -89.0f)m_euler.x = -89.0f;}update_camera_space_vector();update_view_matrix();
}void Perspective_Camera::process_scroll_event(float y_offset)
{m_zoom -= y_offset;if (m_zoom < 1.0f)m_zoom = 1.0f;if (m_zoom > 45.0f)m_zoom = 45.0f;m_fov = m_zoom;update_view_matrix();
}

对 camera 类进行验证:

在 main 函数中创建一个全局的 camera 对象,并用之前我们使用的位置对其进行初始化:

Perspective_Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

回调函数中,我们使用 camera 的成员函数进行处理:

void mouse_callback(GLFWwindow* window, double x_pos, double y_pos)
{if (first_mouse){last_x = x_pos;last_y = y_pos;first_mouse = false;}float x_offset = x_pos - last_x;float y_offset = last_y - y_pos;last_x = x_pos;last_y = y_pos;camera.process_mouse_event(x_offset, y_offset);
}
void scroll_callback(GLFWwindow* window, double x_offset, double y_offset)
{camera.process_scroll_event(static_cast(y_offset));
}
void process_input(GLFWwindow* window)
{//closeif (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);//camera controllerif (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.process_key_event(FORWARD, delta_time);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.process_key_event(BACKWARD, delta_time);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.process_key_event(LEFT, delta_time);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.process_key_event(RIGHT, delta_time);}

然后计算 mvp 矩阵的时候,用 camera 的矩阵代替:

glm::mat4 mvp = camera.get_view_projection_matrix() * model;triangle_shader.set_mat4("u_mvp", mvp); 

运行之后可以得到和上节同样的结果。

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...