【OpenGL学习】Camera 类的抽象

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 flipped
	if (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<float>(y_offset));
}
void process_input(GLFWwindow* window)
{
	//close
	if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
	//camera controller

	if (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); 

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

你可能感兴趣的:(OpenGL,学习)