从零开始写一个opengl渲染器——基础设施搭建篇

基于OpenGL书《计算机图形学编程(使用OpenGL和C++)》中的描述,已经可以在屏幕上输出物体了。但是代码复用的比较多,所以要把复用的代码封装成类,方便后期的维护。先从原始代码中抽象出3个类:窗口类,相机类和控制器类。

窗口类

最开始的窗口代码

GLFWwindow* window = glfwCreateWindow(600, 600, "TEST", nullptr, nullptr);//创建GLFW窗口

glfwMakeContextCurrent(window);//将创建的GLFW窗口与opengl的上下文关联起来

这里的窗口需要指定长度和宽度。写在main函数中,不方便别的地方调用。

为了便于其他方法调用,封装窗口管理类,类中使用单例模式,因为整个程序中只会出现一个窗口。单例出来的实例可以在任意类中调用。

封装代码

//
// Filename: WindowManager.h
// Created by W. Mysterio on 2022-07-04 02:01:39.
// Description:
// Mail: woden3702@gmail.com
//

#ifndef __WINDOWMANAGER_H__
#define __WINDOWMANAGER_H__
#include "GLFW/glfw3.h"

struct WindowSize
{
	unsigned int w;//width
	unsigned int h;//height
};

class WindowManager
{
private:
	static WindowManager* ins_;
	WindowManager();
	WindowSize wSize_;
	GLFWwindow* window_;
public:
	~WindowManager();
	static WindowManager* instance();
	void createMainWindow(unsigned int w, unsigned int h, const char* title, bool fullScreen);
	WindowSize getWindowSize() const;
	GLFWwindow* getWindow() const;
};

#endif //__WINDOWMANAGER_H__
//
// Filename: WindowManager.cpp
// Created by W. Mysterio on 2022-07-04 02:01:39.
// Description:
// Mail: woden3702@gmail.com
//

#include "WindowManager.h"

WindowManager* WindowManager::ins_ = nullptr;

WindowManager::WindowManager() = default;


WindowManager::~WindowManager()
{
	delete ins_;
	ins_ = nullptr;
}

WindowManager* WindowManager::instance()
{
	if (ins_ == nullptr)
		ins_ = new WindowManager();
	return ins_;
}

void WindowManager::createMainWindow(unsigned w, unsigned h, const char* title, bool fullScreen)
{
	wSize_.w = w;
	wSize_.h = h;
	if (fullScreen)
		window_ = glfwCreateWindow(static_cast<int>(wSize_.w), static_cast<int>(wSize_.h), title, glfwGetPrimaryMonitor(), nullptr);
	else
		window_ = glfwCreateWindow(static_cast<int>(wSize_.w), static_cast<int>(wSize_.h), title, nullptr, nullptr);

	glfwSetWindowAttrib(window_, GLFW_RESIZABLE, GLFW_FALSE);//控制是否缩放
	glfwMakeContextCurrent(window_);
	glViewport(0, 0, static_cast<GLsizei>(wSize_.w), static_cast<GLsizei>(wSize_.h));
}

WindowSize WindowManager::getWindowSize() const
{
	return wSize_;
}

GLFWwindow* WindowManager::getWindow() const
{
	return window_;
}

封装好窗口管理类后,修改原来main函数中的代码,先调用createMainWindow方法初始化窗口,后面就可以通过单例方法直接调用窗口。

代码如下:

WindowManager::instance()->createMainWindow(1920, 1080, "test_1", false);
//调用窗口
WindowManager::instance()->getWindow())

相机类

第二个需要的是相机类,有了相机类可以方便后序实现相机移动。原本的代码需要先定义相机的位置坐标、投影矩阵和视图矩阵等信息,现在封装到一个类里,调用的时候会全部初始化。

原来的代码

封装好的相机类

//
// Filename: Camera.h
// Created by W. Mysterio on 2022-07-04 03:22:45.
// Description:
// Mail: woden3702@gmail.com
//

#ifndef __CAMERA_H__
#define __CAMERA_H__
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp>
#include "WindowManager.h"

class Camera
{
private:
	glm::vec3 position_; // 相机位置
	glm::vec3 cameraTarget_; //相机朝向
	glm::vec3 cameraDirection_; //相机的方向
	glm::vec3 cameraUp_; //相机的上方
	glm::vec3 cameraRight_; //相机的右轴

	float fov_; //度
	float farClip_;
	float nearClip_;

	float inWidth_;
	float inHeight_;
	float aspect_;
	glm::mat4 projectionMatrix_;
	glm::mat4 viewMatrix_;
	glm::mat4 modelMatrix_;
	glm::mat4 modelViewMatrix_;


public:

	Camera();
	~Camera();
	void SetPerspectiveCamera(float iFov, float iNearClip, float iFarClip);	void SetNearClip(float iNearClip);
	void setPosition(glm::vec3 ipos);
	void setDirection(glm::vec3 idir);
	float GetNearClip() const;
	void SetFarClip(float iFarClip);
	float GetFarClip() const;
	void SetFov(float iFov);
	float GetFov() const;
	void updateProjectionMatrix();
	void updateViewMatrix();
	void updateModelMatrix();
	void updateModelViewMatrix();
	glm::mat4 getModelViewMatrix() const;
	glm::mat4 getProjectionMatrix() const;
	glm::mat4 getViewMatrix() const;
	glm::vec3 getPosition() const;
	glm::vec3 getRight() const;
	glm::vec3 getDirection() const;
	glm::vec3 getUp() const;
};

#endif //__CAMERA_H__
//
// Filename: Camera.cpp
// Created by W. Mysterio on 2022-07-04 03:22:45.
// Description:
// Mail: woden3702@gmail.com
//

#include "Camera.h"
float toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
glm::vec3 up= glm::vec3(0.0, 1.0f, 0.0f);
glm::vec3 front = glm::vec3(0.0, 0.0f, -1.0f);

Camera::Camera()
{
	position_ = glm::vec3(0.0f, 0.0f, 8.0f);
	cameraTarget_ = glm::vec3(0.0f, 0.0f, 0.0f);
	cameraDirection_ = glm::vec3(0.0, 0.0f, -1.0f);
	cameraUp_ = glm::vec3(0.0, 1.0f, 0.0f);
	cameraRight_ = glm::normalize(glm::cross(cameraDirection_, cameraUp_));

	fov_ = 60.0f; //度
	farClip_ = 1000.0f;
	nearClip_ = 0.1f;
	inWidth_ = WindowManager::instance()->getWindowSize().w;
	inHeight_ = WindowManager::instance()->getWindowSize().h;
	aspect_ = inWidth_ / inHeight_;
	updateProjectionMatrix();
	updateViewMatrix();
}

Camera::~Camera() = default;

void Camera::updateProjectionMatrix()
{
	projectionMatrix_= glm::perspective(toRadians(fov_), aspect_, nearClip_, farClip_);
}

void Camera::SetNearClip(float iNearClip)
{
	nearClip_ = iNearClip;
}

void Camera::setPosition(glm::vec3 ipos)
{
	position_ = ipos;
}

void Camera::setDirection(glm::vec3 idir)
{
	cameraDirection_ = idir;
}

float Camera::GetNearClip() const
{
	return nearClip_;
}

void Camera::SetFarClip(float iFarClip)
{
	farClip_ = iFarClip;
}

float Camera::GetFarClip() const
{
	return farClip_;
}

void Camera::SetFov(float iFov)
{
	fov_ = iFov;
}

float Camera::GetFov() const
{
	return fov_;
}

void Camera::updateViewMatrix()
{

	viewMatrix_= glm::lookAt(position_,position_+cameraDirection_,cameraUp_);
}

void Camera::updateModelMatrix()
{
	modelMatrix_ = glm::translate(glm::mat4(1.0f),cameraTarget_);
}

void Camera::updateModelViewMatrix()
{
	modelViewMatrix_ = viewMatrix_ * modelMatrix_;
}

glm::mat4 Camera::getModelViewMatrix() const
{
	return modelViewMatrix_;
}

glm::mat4 Camera::getProjectionMatrix() const
{
	return projectionMatrix_;
}

glm::mat4 Camera::getViewMatrix() const
{
	return viewMatrix_;
}

glm::vec3 Camera::getPosition() const
{
	return position_;
}

glm::vec3 Camera::getRight() const
{
	return glm::normalize(glm::cross(cameraDirection_, cameraUp_));
}

glm::vec3 Camera::getDirection() const
{
	return cameraDirection_;
}

glm::vec3 Camera::getUp() const
{
	return cameraUp_;
}

目前相机类仍然是比较复杂,后期需要重构,可能还要抽象出一个物体类,封装坐标等信息

如果想要在别的函数中直接获取已有的相机对象,则需要一个相机管理类,管理创建删除等操作。

//
// Filename: CameraManager.h
// Created by W. Mysterio on 2022-07-07 03:20:01.
// Description:
// Mail: woden3702@gmail.com
//

#ifndef __CAMERAMANAGER_H__
#define __CAMERAMANAGER_H__
#include <stack>
#include <memory>

#include "Camera.h"

class CameraManager
{
private:
	CameraManager();
	static CameraManager* ins_;
	std::stack<std::shared_ptr<Camera>> cams_;
public:

	~CameraManager();
	static CameraManager* instance();
	std::shared_ptr<Camera> push();
	void pop();
	std::shared_ptr<Camera> getCurCamera();
};

#endif //__CAMERAMANAGER_H__
//
// Filename: CameraManager.cpp
// Created by W. Mysterio on 2022-07-07 03:20:01.
// Description:
// Mail: woden3702@gmail.com
//

#include "CameraManager.h"

CameraManager* CameraManager::ins_ = nullptr;

CameraManager::CameraManager() = default;

CameraManager::~CameraManager()
{
	ins_ = nullptr;
}

CameraManager* CameraManager::instance()
{
	if (ins_ == nullptr)
		ins_ = new CameraManager();
	return ins_;
}

std::shared_ptr<Camera> CameraManager::push()
{
	std::shared_ptr<Camera> newCam = std::make_shared<Camera>();
	cams_.push(newCam);
	return newCam;
}

void CameraManager::pop()
{
	cams_.pop();
}

std::shared_ptr<Camera> CameraManager::getCurCamera()
{
	return cams_.top();
}

管理类都长一个样子,后面的管理类大概都是这个模式。

控制类

创建好相机后,要实现移动相机,需要绑定一些回调函数。为了方便管理,把这些操作封装成一个控制类。

//
// Filename: Controller.h
// Created by W. Mysterio on 2022-07-07 03:58:35.
// Description:
// Mail: woden3702@gmail.com
//

#ifndef __CONTROLLER_H__
#define __CONTROLLER_H__
#include <glm/vec3.hpp>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective

#include "WindowManager.h"
#define FIXEDUPDATE_TIME 0.01f//In seconds

class Controller
{
private:
	double lastMousePos[2];
	int curMouseButton;
	float moveSpeed=3.0f;
	glm::vec3 moveVec;
	float rotSensitivity[2];
	float mouseMovSensitivity[3];
	float offsetX, offsetY;
	float yaw, pitch;//两种欧拉角  俯仰角(Pitch)、偏航角(Yaw)
public:
	Controller();
	void init();
	void update();
	void MouseMotionCallback(GLFWwindow* window, double x, double y);
	void MouseKeyCallback(GLFWwindow* window, int button, int state, int mods);
	void KeyInputCallback(GLFWwindow* window, int key, int scanCode, int action, int mods);
};

#endif //__CONTROLLER_H__
//
// Filename: Controller.cpp
// Created by W. Mysterio on 2022-07-07 03:58:35.
// Description:
// Mail: woden3702@gmail.com
//

#include "Controller.h"

#include "CameraManager.h"

Controller::Controller()
{
	init();
}

void Controller::init()
{
	rotSensitivity[0] = 0.05f;
	rotSensitivity[1] = 0.03f;
	mouseMovSensitivity[0] = 0.02f;
	mouseMovSensitivity[1] = 0.01f;
	mouseMovSensitivity[2] = 0.1f;
	moveVec = glm::vec3(0.0f, 0.0f, 0.0f);
	yaw = -90.0f;
	pitch = 0.0f;
}

void Controller::update()
{
	if (curMouseButton == GLFW_MOUSE_BUTTON_RIGHT)
	{
		glm::vec3 curPos = CameraManager::instance()->getCurCamera()->getPosition();
		// printf("按下右键 position: ");
		// printf("(%f,%f,%f)\n", curPos.x, curPos.y, curPos.z);
		// printf("sasd%f\n", moveVec.x);
		curPos = curPos + (normalize(CameraManager::instance()->getCurCamera()->getDirection()) * moveSpeed * moveVec.x +
			normalize(CameraManager::instance()->getCurCamera()->getRight()) * moveSpeed * moveVec.y +
			normalize(CameraManager::instance()->getCurCamera()->getUp()) * moveSpeed * moveVec.z) * FIXEDUPDATE_TIME;
		// printf("______处理完后: ");
		// printf("(%f,%f,%f)\n", curPos.x, curPos.y, curPos.z);
		CameraManager::instance()->getCurCamera()->setPosition(curPos);
	}
}

void Controller::MouseMotionCallback(GLFWwindow* window, double x, double y)
{
	switch (curMouseButton)
	{
	case GLFW_MOUSE_BUTTON_RIGHT:
	{
			//参考https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/#_6
		offsetX = x - lastMousePos[0];
		offsetY = lastMousePos[1] - y;
		// printf("(%f,%f)\n", x, y);
		// printf("(%f,%f)\n", offsetX, offsetY);
		offsetX *= rotSensitivity[0];
		offsetY *= rotSensitivity[1];
		yaw += offsetX;
		pitch += offsetY;

		if (pitch > 89.0f)
			pitch = 89.0f;
		if (pitch < -89.0f)
			pitch = -89.0f;

		glm::vec3 front;
		front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
		front.y = sin(glm::radians(pitch));
		front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
		CameraManager::instance()->getCurCamera()->setDirection(normalize(front));
	}
	break;
	}
	//记录鼠标松开的位置
	lastMousePos[0] = x;
	lastMousePos[1] = y;
}

void Controller::MouseKeyCallback(GLFWwindow* window, int button, int state, int mods)
{
	switch (state)
	{
	case GLFW_PRESS:
	{
		curMouseButton = button;
	}
	break;
	case GLFW_RELEASE:
	{
		curMouseButton = -1;
	}
	break;
	}

	glfwGetCursorPos(window, &lastMousePos[0], &lastMousePos[1]);
}

void Controller::KeyInputCallback(GLFWwindow* window, int key, int scanCode, int action, int mods)
{
	switch (key)
	{
	default:
		break;
	case GLFW_KEY_W:
		moveVec.x = (action == GLFW_PRESS || action == GLFW_REPEAT) ? 1.0f : 0.0f;
		break;
	case GLFW_KEY_S:
		moveVec.x = (action == GLFW_PRESS || action == GLFW_REPEAT) ? -1.0f : 0.0f;
		break;
	case GLFW_KEY_A:
		moveVec.y = (action == GLFW_PRESS || action == GLFW_REPEAT) ? -1.0f : 0.0f;
		break;
	case GLFW_KEY_D:
		moveVec.y = (action == GLFW_PRESS || action == GLFW_REPEAT) ? 1.0f : 0.0f;
		break;
	case GLFW_KEY_E:
		moveVec.z = (action == GLFW_PRESS || action == GLFW_REPEAT) ? 1.0f : 0.0f;
		break;
	case GLFW_KEY_Q:
		moveVec.z = (action == GLFW_PRESS || action == GLFW_REPEAT) ? -1.0f : 0.0f;
		break;
	}


	switch (key)
	{
	default:
		break;
	case GLFW_KEY_W:
		if (action == GLFW_RELEASE)
		{
			moveVec.x = 0.0f;
		}
		break;
	case GLFW_KEY_S:
		if (action == GLFW_RELEASE)
		{
			moveVec.x = 0.0f;
		}
		break;
	case GLFW_KEY_A:
		if (action == GLFW_RELEASE)
		{
			moveVec.y = 0.0f;
		}
		break;
	case GLFW_KEY_D:
		if (action == GLFW_RELEASE)
		{
			moveVec.y = 0.0f;
		}
		break;
	case GLFW_KEY_E:
		if (action == GLFW_RELEASE)
		{
			moveVec.z = 0.0f;
		}
		break;
	case GLFW_KEY_Q:
		if (action == GLFW_RELEASE)
		{
			moveVec.z = 0.0f;
		}
		break;
	case GLFW_KEY_SPACE:
		if (action == GLFW_PRESS)
		{
			//回到原位
			CameraManager::instance()->getCurCamera()->setPosition(glm::vec3(0.0f, 0.0f, 8.0f));
			CameraManager::instance()->getCurCamera()->setDirection(glm::vec3(0.0f, 0.0f, -1.0f));
			CameraManager::instance()->getCurCamera()->updateViewMatrix();
		}
	}
}

在该类中,实现了按住鼠标右键的同时按住wasdqe方向键实现相机的前后左右上下移动,操作方式和unity中的一致。

主要逻辑是在主循环中调用控制类中的update方法,该方法实现的功能是按住鼠标右键获取当前相机的位置坐标,通过回调函数监听各个坐标的改变情况,再实现位置坐标的改变,目前相机类缺少旋转坐标,后期要想办法加上去。

后期可能还有别的种类的控制类,还需要创建一个控制类的父类,绑定鼠标按键等重复操作。

实现控制管理类:

//
// Filename: ControllerManager.h
// Created by W. Mysterio on 2022-07-07 04:20:43.
// Description:
// Mail: woden3702@gmail.com
//

#ifndef __CONTROLLERMANAGER_H__
#define __CONTROLLERMANAGER_H__
#include <stack>
#include <memory>
#include "Controller.h"

class ControllerManager
{
private:
	ControllerManager();
	static ControllerManager* ins_;
	std::stack<std::shared_ptr<Controller>> ctrls_;
public:
	~ControllerManager();
	static ControllerManager* instance();
	void push();
	void push(std::shared_ptr<Controller>);
	void pop();
	std::shared_ptr<Controller> getCurController();
};

#endif //__CONTROLLERMANAGER_H__
//
// Filename: ControllerManager.cpp
// Created by W. Mysterio on 2022-07-07 04:20:43.
// Description:
// Mail: woden3702@gmail.com
//

#include "ControllerManager.h"
ControllerManager* ControllerManager::ins_ = nullptr;

ControllerManager::ControllerManager()= default;

ControllerManager::~ControllerManager()
{
	ins_ = nullptr;
}

ControllerManager* ControllerManager::instance()
{
	if (ins_ == nullptr)
		ins_ = new ControllerManager();
	return ins_;
}

void ControllerManager::push()
{
	auto newCtrl = std::make_shared<Controller>();
	ctrls_.push(newCtrl);
}

void ControllerManager::push(std::shared_ptr<Controller> controller)
{
	ctrls_.push(controller);
}

void ControllerManager::pop()
{
	ctrls_.pop();
}

std::shared_ptr<Controller> ControllerManager::getCurController()
{
	return ctrls_.top();
}

代码参考haha2345/myTinyOpenglRender (github.com)

posted @ 2022-07-13 19:28  woden  阅读(630)  评论(0编辑  收藏  举报