8. 摄像机类实现
效果,按下wasdqr可以平移,鼠标拖动可以旋转,滑轮可以缩放
照相机类头文件
#pragma once #include <QVector3D> #include <QMatrix4x4> #include <QKeyEvent> 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.01f; const float ZOOM = 45.0f; class Camera { public: //相机位置,相机的上轴,yaw关于y轴的旋转向左向右看的程度,pitch关于x轴的旋转,向上向下看的俯仰角 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(); QMatrix4x4 getViewMatrix(); void processMouseMovement(float xoffset, float yoffset, bool constraintPitch = true); void processMouseScroll(float yoffset); void processInput(float dt); QVector3D position;//摄像机位置向量 QVector3D worldUp;//世界坐标的上轴 QVector3D front;//摄像机的方向向量,其实就是-position QVector3D up;//上向量 QVector3D right;//右向量 //俯仰角 float picth; //偏航角 float yaw; float movementSpeed;//鼠标速度 float mouseSensitivity;//鼠标灵敏度 float zoom;////缩放 之前有一个函数projection.perspective定义了视角的缩放,在那边会用到 bool keys[1024]; private: void updateCameraVectors(); void processKeyboard(Camera_Movement direction, float deltaTime); };
照相机类源文件
#include "Camera.h" #include <QDebug> //传参 Camera::Camera(QVector3D position, QVector3D up, float yaw, float pitch) : position(position), worldUp(up), front(-position), picth(pitch), yaw(yaw), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM) { this->updateCameraVectors(); for (uint i = 0; i != 1024; ++i) keys[i] = false; } Camera::~Camera() { } // Returns the view matrix calculated using Euler Angles and the LookAt Matrix QMatrix4x4 Camera::getViewMatrix() { QMatrix4x4 view; //(const QVector3D& eye, const QVector3D& center, const QVector3D& up); view.lookAt(this->position, this->position + this->front, this->up); return view; } //键盘按下事件 void Camera::processKeyboard(Camera_Movement direction, float deltaTime) { float velocity = this->movementSpeed * deltaTime;//速度 if (direction == FORWARD) this->position += this->front * velocity; if (direction == BACKWARD) this->position -= this->front * velocity; if (direction == LEFT) this->position -= this->right * velocity; if (direction == RIGHT) this->position += this->right * velocity; if (direction == UP) this->position += this->worldUp * velocity; if (direction == DOWN) this->position -= this->worldUp * velocity; } //鼠标移动事件 void Camera::processMouseMovement(float xoffset, float yoffset, bool constraintPitch) { xoffset *= this->mouseSensitivity; yoffset *= this->mouseSensitivity; this->yaw += xoffset; this->picth += yoffset; if (constraintPitch) { if (this->picth > 89.0f) this->picth = 89.0f; if (this->picth < -89.0f) this->picth = -89.0f; } this->updateCameraVectors(); } // 该参数表示竖直滚动大小,因为45时默认的视野值,所以把缩放级别限制在1到45 void Camera::processMouseScroll(float yoffset) { //小于45°时 if (this->zoom >= 1.0f && this->zoom <= 45.0f) this->zoom -= yoffset; if (this->zoom > 45.0f) this->zoom = 45.0f; if (this->zoom < 1.0f) this->zoom = 1.0f; } //按键移动 void Camera::processInput(float dt) { if (keys[Qt::Key_W]) processKeyboard(FORWARD, dt); if (keys[Qt::Key_S]) processKeyboard(BACKWARD, dt); if (keys[Qt::Key_A]) processKeyboard(LEFT, dt); if (keys[Qt::Key_D]) processKeyboard(RIGHT, dt); if (keys[Qt::Key_E]) processKeyboard(UP, dt); if (keys[Qt::Key_Q]) processKeyboard(DOWN, dt); } void Camera::updateCameraVectors() { ////////////////////////////////////////////转动需要 // 通过俯仰角和偏航角得到新的xyz位置 QVector3D front; front.setX(cos(this->yaw) * cos(this->picth)); front.setY(sin(this->picth)); front.setZ(sin(this->yaw) * cos(this->picth)); this->front = front.normalized(); ////////////////////////////////////////////移动需要 //利用世界上轴和相机向量叉乘获得右轴 //右轴等于上向量,鼠标向左的时候我们希望它向右移动,所以是front叉乘worldUp反之就反着叉乘 this->right = QVector3D::crossProduct(this->front, this->worldUp).normalized(); //同上 this->up = QVector3D::crossProduct(this->right, this->front).normalized(); }
什么地方用到了照相机类
在构造函数中初始化其指针
在PaintGL函数中
在事件重载的函数中
OpenGL类头文件
#pragma once #include <QOpenGLWidget> #include <QOpenGLShaderProgram> #include <QOpenGLFunctions> #include <QOpenGLVertexArrayObject> #include <QOpenGLBuffer> #include <QOpenGLTexture> #include "Camera.h" class QtFunctionWidget : public QOpenGLWidget, protected QOpenGLFunctions { public: QtFunctionWidget(QWidget *parent = nullptr); ~QtFunctionWidget() Q_DECL_OVERRIDE; protected: virtual void initializeGL() Q_DECL_OVERRIDE; virtual void resizeGL(int w, int h) Q_DECL_OVERRIDE; virtual void paintGL() Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; private: QOpenGLShaderProgram shaderProgram; QOpenGLBuffer vbo; QOpenGLVertexArrayObject vao; QOpenGLTexture *texture1 = nullptr; QOpenGLTexture *texture2 = nullptr; QTimer* m_pTimer = nullptr; int m_nTimeValue = 0; // camera std::unique_ptr<Camera> camera;//相机指针 两个unique_ptr指针不能指向同一个对象,不能复制只能移动 bool m_bLeftPressed; QPoint m_lastPos;//上次的点 };
OpenGL类源文件
#include "QtFunctionWidget.h" #include <QDebug> #include <QTimer> QtFunctionWidget::QtFunctionWidget(QWidget *parent) : QOpenGLWidget(parent), vbo(QOpenGLBuffer::VertexBuffer) { camera = std::make_unique<Camera>(QVector3D(5.0f, 0.0f, 10.0f));//创建并且返回unique_ptr对象 m_bLeftPressed = false; //重绘 m_pTimer = new QTimer(this); connect(m_pTimer, &QTimer::timeout, this, [=] { m_nTimeValue += 1; update(); }); m_pTimer->start(40); } QtFunctionWidget::~QtFunctionWidget() { //多线程调用保护 makeCurrent(); //glDeleteBuffers vbo.destroy(); //glDeleteVertexArrays vao.destroy(); delete texture1; delete texture2; //退出保护 doneCurrent(); } void QtFunctionWidget::initializeGL() { this->initializeOpenGLFunctions(); bool success = shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, "textures.vert"); if (!success) { qDebug() << "shaderProgram addShaderFromSourceFile failed!" << shaderProgram.log(); return; } success = shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, "textures.frag"); if (!success) { qDebug() << "shaderProgram addShaderFromSourceFile failed!" << shaderProgram.log(); return; } success = shaderProgram.link(); if (!success) { qDebug() << "shaderProgram link failed!" << shaderProgram.log(); } //VAO,VBO data float vertices[] = { -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f }; //绑定vao QOpenGLVertexArrayObject::Binder vaoBind(&vao); //创建vbo vbo.create(); //绑定vbo vbo.bind(); //分配数组内存空间 vbo.allocate(vertices, sizeof(vertices)); //和glVertexAttribPointer有区别 //glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer) // void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0);//不需要填是否标准化 shaderProgram.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);//位置0,数据类型float,起始从0开始,元组大小为3个,步长为5个float shaderProgram.enableAttributeArray(0); shaderProgram.setAttributeBuffer(1, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5); shaderProgram.enableAttributeArray(1); vbo.release();//vbo操作完毕,接下来就没有对vbo内存的操作了 //纹理一 texture1 = new QOpenGLTexture(QImage("container.jpg"), QOpenGLTexture::GenerateMipMaps); if (!texture1->isCreated()) { qDebug() << "Failed to load texture"; } //纹理的样式 texture1->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); texture1->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); texture1->setMinificationFilter(QOpenGLTexture::Linear); texture1->setMagnificationFilter(QOpenGLTexture::Linear); //纹理二 texture2 = new QOpenGLTexture(QImage("awesomeface.png").mirrored(true, true), QOpenGLTexture::GenerateMipMaps); if (!texture2->isCreated()) { qDebug() << "Failed to load texture"; } texture2->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); texture2->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); texture2->setMinificationFilter(QOpenGLTexture::Linear); texture2->setMagnificationFilter(QOpenGLTexture::Linear); // tell opengl for each sampler to which texture unit it belongs to (only has to be done once) shaderProgram.bind();//纹理的值传递给着色器 shaderProgram.setUniformValue("texture1", 0);//0代表第一个纹理 shaderProgram.setUniformValue("texture2", 1);//1代表第二个纹理 glEnable(GL_DEPTH_TEST);//开启深度测试 } void QtFunctionWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); } //位置数组 static QVector3D cubePositions[] = { QVector3D(0.0f, 0.0f, 0.0f), QVector3D(2.0f, 5.0f, -15.0f), QVector3D(-1.5f, -2.2f, -2.5f), QVector3D(-3.8f, -2.0f, -12.3f), QVector3D(2.4f, -0.4f, -3.5f), QVector3D(-1.7f, 3.0f, -7.5f), QVector3D(1.3f, -2.0f, -2.5f), QVector3D(1.5f, 2.0f, -2.5f), QVector3D(1.5f, 0.2f, -1.5f), QVector3D(-1.3f, 1.0f, -1.5f) }; void QtFunctionWidget::paintGL() { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->processInput(1.0f);//本来参数是用来计算电脑两帧之间的时间的,这里用1代替 //绑定纹理 glActiveTexture(GL_TEXTURE0); texture1->bind(); glActiveTexture(GL_TEXTURE1); texture2->bind(); //想要着色器调用我们的c++程序就需要先bind shaderProgram.bind(); QMatrix4x4 projection; //perspective的第一个参数是缩放参数 projection.perspective(camera->zoom, 1.0f * width() / height(), 0.1f, 100.f); shaderProgram.setUniformValue("projection", projection); //通过照相机对象的内置函数获取视图数组 shaderProgram.setUniformValue("view", camera->getViewMatrix()); {//绑定vao QOpenGLVertexArrayObject::Binder vaoBind(&vao); for (unsigned int i = 0; i < 10; i++) { // 对每个物体都要计算model矩阵 QMatrix4x4 model; model.translate(cubePositions[i]);//平移 float angle = (i + 1.0f) * m_nTimeValue; model.rotate(angle, QVector3D(1.0f, 0.3f, 0.5f));//旋转 shaderProgram.setUniformValue("model", model);//把数据传递给着色器 glDrawArrays(GL_TRIANGLES, 0, 36); } } //解除绑定 texture1->release(); texture2->release(); shaderProgram.release(); } //键盘按下事件 //keys是一个bool的数组,通过以下两个函数来判断键盘是否按下 void QtFunctionWidget::keyPressEvent(QKeyEvent *event) { int key = event->key(); if (key >= 0 && key < 1024) camera->keys[key] = true; } void QtFunctionWidget::keyReleaseEvent(QKeyEvent *event) { int key = event->key(); if (key >= 0 && key < 1024) camera->keys[key] = false; } //鼠标是否按下事件 void QtFunctionWidget::mousePressEvent(QMouseEvent *event) { //按下该键把参数设置成true if (event->button() == Qt::LeftButton) { m_bLeftPressed = true; m_lastPos = event->pos(); } } //鼠标释放函数,把该值设置成false void QtFunctionWidget::mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event);//Q_UNUSED() 没有实质性的作用,用来避免编译器警告 m_bLeftPressed = false; } //鼠标移动事件 void QtFunctionWidget::mouseMoveEvent(QMouseEvent *event) { //如果鼠标按下了就触发鼠标移动事件 if (m_bLeftPressed) { int xpos = event->pos().x(); int ypos = event->pos().y(); //x,y移动的距离 int xoffset = xpos - m_lastPos.x(); int yoffset = m_lastPos.y() - ypos;//qt的y坐标往下是正,opengl y坐标往上是正,openGL远点在中间,QT在左上角 //重置上一次的移动位置 m_lastPos = event->pos(); camera->processMouseMovement(xoffset, yoffset); } } //滚轮事件 void QtFunctionWidget::wheelEvent(QWheelEvent *event) { QPoint offset = event->angleDelta(); camera->processMouseScroll(offset.y() / 20.0f); }
顶点着色器
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main(){ gl_Position = projection * view * model * vec4(aPos, 1.0f); TexCoord = aTexCoord; }
片段着色器
#version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D texture1; uniform sampler2D texture2; void main() { FragColor = mix(texture2D(texture1, TexCoord), texture2D(texture2, TexCoord), 0.2f); }