在 OpenGL 中,我们通过数学方法构建出一个视图矩阵模拟摄像机,也可以通过定义一些变量,例如当前坐标和视角朝向,然后接收用户的键盘和鼠标操作,根据操作动态的修改这些变量,最终返回给 OpenGL 一个基于这些信息的视图矩阵。
摄像机类
按键枚举
将用户移动方向进行枚举,可以避免特定与窗口系统的输入方式
| enum Camera_Movement { FORWARD, BACKWARD, LEFT, RIGHT, UP, DOWN };
|
Camera.h
下面对主要使用的函数进行介绍:
有两个构造函数,表示根据不同的参数初始化视图矩阵
Camera(QVector3D position = QVector3D(0.0f, 0.0f, 0.0f), QVector3D up = QVector3D(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH)
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch)
GetViewMatrix() 函数可以直接返回当前状态下的视图矩阵。
ProcessKeyboard(Camera_Movement direction, float deltaTime) 函数用于处理键盘输入,参数为当前按键(按键枚举类)和 时间差 deltaTime
ProcessMouseMovement(float xoffset, float yoffset) 函数用于处理鼠标输入,参数为 x 方向的位移 xoffset 和 y 方向的位移 yoffset
ProcessMouseScroll(float yoffset) 函数用于处理鼠标滚轮输入,参数为滚动位移 yoffset
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
| #ifndef CAMERA_H #define CAMERA_H
#include <QMatrix4x4> #include <vector> #include <complex>
enum Camera_Movement { FORWARD, BACKWARD, LEFT, RIGHT, UP, DOWN };
const float YAW = -90.0f; const float PITCH = 0.0f; const float SPEED = 1.0f; const float SENSITIVITY = 0.1f; const float ZOOM = 45.0f;
class Camera { public: QVector3D Position; QVector3D Front; QVector3D Up; QVector3D Right; QVector3D WorldUp; float Yaw; float Pitch; float MovementSpeed = SPEED; float MouseSensitivity = SENSITIVITY; float Zoom = ZOOM;
Camera(QVector3D position = QVector3D(0.0f, 0.0f, 0.0f), QVector3D up = QVector3D(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) { Position = position; WorldUp = up; Yaw = yaw; Pitch = pitch; updateCameraVectors(); } Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) { Position = QVector3D(posX, posY, posZ); WorldUp = QVector3D(upX, upY, upZ); Yaw = yaw; Pitch = pitch; updateCameraVectors(); }
QMatrix4x4 GetViewMatrix() { QMatrix4x4 theMatrix; theMatrix.lookAt(Position, Position + Front, Up); return theMatrix; }
void ProcessKeyboard(Camera_Movement direction, float deltaTime) { float velocity = MovementSpeed * deltaTime; if(direction == FORWARD) { Position += Front * velocity; } if(direction == BACKWARD) { Position -= Front * velocity; } if(direction == LEFT) { Position -= Right * velocity; } if(direction == RIGHT) { Position += Right * velocity; } if(direction == UP) { Position += Up * velocity; } if(direction == DOWN) { Position -= Up * velocity; }
}
void ProcessMouseMovement(float xoffset, float yoffset) { xoffset *= MouseSensitivity; yoffset *= MouseSensitivity; Yaw += xoffset; Pitch += yoffset; if (true) { if (Pitch > 89.0f) { Pitch = 89.0f; } if (Pitch < -89.0f) { Pitch = -89.0f; } } updateCameraVectors(); }
void ProcessMouseScroll(float yoffset) { Zoom -= (float)yoffset; if (Zoom < 1.0f) { Zoom = 1.0f; } if (Zoom > 75.0f) { Zoom = 75.0f; } } private: void updateCameraVectors() { float PI = 3.14159265f; QVector3D front; front.setX(cos(Yaw * PI / 180.0) * cos(Pitch * PI / 180.0)); front.setY(sin(Pitch * PI / 180.0)); front.setZ(sin(Yaw * PI / 180.0) * cos(Pitch * PI / 180.0)); front.normalize(); Front = front; Right = QVector3D::crossProduct(Front, WorldUp); Right.normalize(); Up = QVector3D::crossProduct(Right, Front); Up.normalize(); } };
#endif
|
如何使用摄像机类
引入成员变量并初始化
- 在 OpenGLWidget 类中将摄像机类作为成员变量加入
| private: Camera m_camera;
|
- 定义一个相机初始位置 viewInitPos
| QVector3D viewInitPos(0.0f, 5.0f, 10.0f);
|
- 在 OpenGLWidget 类的构造函数中设置 m_camera 的位置
| OpenGLWidget::OpenGLWidget(QWidget *parent) : QOpenGLWidget(parent) { m_camera.Position = viewInitPos; }
|
重写鼠标、键盘事件处理类
在 OpenGLWidget 类的 protected 中加入 wheelEvent、keyPressEvent、mouseMoveEvent、mousePressEvent 这四个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| include <QOpenGLWidget> #include <QOpenGLFunctions_4_5_Core> #include <QOpenGLVertexArrayObject> #include <QOpenGLBuffer>
class OpenGLWidget : public QOpenGLWidget, QOpenGLFunctions_4_5_Core { Q_OBJECT public: OpenGLWidget(QWidget *parent); ~OpenGLWidget(); protected: virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); void wheelEvent(QWheelEvent *event); void keyPressEvent(QKeyEvent *event); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); private: QOPenGLVertexArrayObject m_vao; QOpenGLBuffer m_vbo; }
|
重写这几个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| void TreeOpenGLWidget::wheelEvent(QWheelEvent *event) { m_camera.ProcessMouseScroll(event->angleDelta().y() / 120); }
void TreeOpenGLWidget::keyPressEvent(QKeyEvent *event) { float deltaTime = timeOutmSec / 50.0f; switch (event->key()) { case Qt::Key_W: m_camera.ProcessKeyboard(FORWARD, deltaTime); break; case Qt::Key_S: m_camera.ProcessKeyboard(BACKWARD, deltaTime); break; case Qt::Key_A: m_camera.ProcessKeyboard(LEFT, deltaTime); break; case Qt::Key_D: m_camera.ProcessKeyboard(RIGHT, deltaTime); break; case Qt::Key_Q: m_camera.ProcessKeyboard(UP, deltaTime); break; case Qt::Key_E: m_camera.ProcessKeyboard(DOWN, deltaTime); break; case Qt::Key_Space: m_camera.Position = viewInitPos; break; default: break; } }
void TreeOpenGLWidget::mouseMoveEvent(QMouseEvent *event) { makeCurrent(); if(event->buttons() & Qt::RightButton) { auto currentPos = event->pos(); QPoint deltaPos = currentPos - lastPos; lastPos = currentPos; m_camera.ProcessMouseMovement(deltaPos.x(), -deltaPos.y()); } doneCurrent(); }
void TreeOpenGLWidget::mousePressEvent(QMouseEvent *event) { makeCurrent(); lastPos = event->pos(); doneCurrent(); }
|
绘制前获取信息
| m_projection.perspective(m_camera.Zoom, (float)width()/height(),0.1,100);
m_view = m_camera.GetViewMatrix();
|
现在就可以使用键盘、鼠标、滚轮控制显示的视图了~