如何从零开始搭建一个完整的OpenGL渲染器基础设施?

2026-04-11 06:301阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2633个文字,预计阅读时间需要11分钟。

如何从零开始搭建一个完整的OpenGL渲染器基础设施?

基于OpenGL的书籍《计算机图形学编程(使用OpenGL和C++)》中描述,已经在屏幕上输出了物体了。但是代码复用性较高,因此需要将复用的代码封装成类,以便于后续的维护。以下是从原始代码中提取的封装过程:

cpp// 原始代码片段void drawObject() { // ...绘制物体的代码...}

// 封装成类class Object {public: void draw() { // ...绘制物体的代码... }};

// 使用封装后的类Object myObject;myObject.draw();

基于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方法初始化窗口,后面就可以通过单例方法直接调用窗口。

如何从零开始搭建一个完整的OpenGL渲染器基础设施?

代码如下:

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: { //参考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)

本文共计2633个文字,预计阅读时间需要11分钟。

如何从零开始搭建一个完整的OpenGL渲染器基础设施?

基于OpenGL的书籍《计算机图形学编程(使用OpenGL和C++)》中描述,已经在屏幕上输出了物体了。但是代码复用性较高,因此需要将复用的代码封装成类,以便于后续的维护。以下是从原始代码中提取的封装过程:

cpp// 原始代码片段void drawObject() { // ...绘制物体的代码...}

// 封装成类class Object {public: void draw() { // ...绘制物体的代码... }};

// 使用封装后的类Object myObject;myObject.draw();

基于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方法初始化窗口,后面就可以通过单例方法直接调用窗口。

如何从零开始搭建一个完整的OpenGL渲染器基础设施?

代码如下:

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: { //参考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)