美文网首页OpenGL
基于Qt的OpenGL学习(6)—— 摄像机

基于Qt的OpenGL学习(6)—— 摄像机

作者: 玖零儛 | 来源:发表于2019-08-12 17:27 被阅读0次

    简介

    要学习OpenGL的话,强烈安利这个教程JoeyDeVries的learnopengl,这里是中文翻译好的版本。教程中使用OpenGL是通过GLFW这个库,而在Qt中对OpenGL封装得很好,并且和GUI以及IO相关的处理Qt更便捷,学习起来更轻松。这里就对每篇教程,在Qt在分别直接使用OpenGL的函数和Qt封装好的类以作对比。
    教程中使用的OpenGL版本为3.3,在Qt中需要使用此版本的OpenGL只需要继承类QOpenGLFunctions_3_3_Core即可。如果为了在不同设备上都能用OpenGL的话,Qt提供了类QOpenGLFunctions,这个类包含了大部分公共的函数,可能会有个别函数不能用。

    对比说明

    教程地址

    原教程地址,相关知识可以点击链接学习。
    我的工程地址,每篇教程一个commit,可以切换着看,查看本篇代码 git checkout v1.6,喜欢就点个Star吧~

    不同点 (仅列出新增)

    1. Qt中封装的OpenGL相关的类之前基本都对比完了,其余部分基本还是用glxxx()的函数,后面可能就只在QtFunctionWidget.cpp中修改了。
    2. 我的更新是通过QTimer调用update()实现的,就没有计算教程中的时间差了。

    运行结果

    运行结果

    修改的文件

    QtFunctionWidget.h

    #ifndef QTFUNCTIONWIDGET_H
    #define QTFUNCTIONWIDGET_H
    
    #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;
        bool m_bLeftPressed;
        QPoint m_lastPos;
    };
    
    #endif // QTFUNCTIONWIDGET_H
    

    QtFunctionWidget.cpp

    #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));
        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();
    
        vbo.destroy();
        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
        };
    
        QOpenGLVertexArrayObject::Binder vaoBind(&vao);
    
        vbo.create();
        vbo.bind();
        vbo.allocate(vertices, sizeof(vertices));
    
        // position attribute
        int attr = -1;
        attr = shaderProgram.attributeLocation("aPos");
        shaderProgram.setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);
        shaderProgram.enableAttributeArray(attr);
        // texture coord attribute
        attr = shaderProgram.attributeLocation("aTexCoord");
        shaderProgram.setAttributeBuffer(attr, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5);
        shaderProgram.enableAttributeArray(attr);
    
        // texture 1
        // ---------
        texture1 = new QOpenGLTexture(QImage(":/container.jpg"), QOpenGLTexture::GenerateMipMaps);
        if(!texture1->isCreated()){
            qDebug() << "Failed to load texture";
        }
        // set the texture wrapping parameters
        texture1->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
        texture1->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
        // set texture filtering parameters
        texture1->setMinificationFilter(QOpenGLTexture::Linear);
        texture1->setMagnificationFilter(QOpenGLTexture::Linear);
    
        // texture 2
        // ---------
        texture2 = new QOpenGLTexture(QImage(":/awesomeface.png").mirrored(true, true), QOpenGLTexture::GenerateMipMaps);
        if(!texture2->isCreated()){
            qDebug() << "Failed to load texture";
        }
        // set the texture wrapping parameters
        texture2->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
        texture2->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
        // set texture filtering parameters
        texture2->setMinificationFilter(QOpenGLTexture::Linear);
        texture1->setMagnificationFilter(QOpenGLTexture::Linear);
    
        // tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
        shaderProgram.bind();   // don't forget to activate/use the shader before setting uniforms!
        shaderProgram.setUniformValue("texture1", 0);
        shaderProgram.setUniformValue("texture2", 1);
    
        vbo.release();
    
        // configure global opengl state
        // -----------------------------
        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); // also clear the depth buffer now!
    
        camera->processInput(1.0f);
    
        // bind textures on corresponding texture units
        glActiveTexture(GL_TEXTURE0);
        texture1->bind();
        glActiveTexture(GL_TEXTURE1);
        texture2->bind();
    
        shaderProgram.bind();
    
        QMatrix4x4 projection;
        projection.perspective(camera->zoom, 1.0f * width() / height(), 0.1f, 100.f);
        shaderProgram.setUniformValue("projection", projection);
    
        // camera/view transformation
        shaderProgram.setUniformValue("view", camera->getViewMatrix());
    
        {// render box
            QOpenGLVertexArrayObject::Binder vaoBind(&vao);
    
            for (unsigned int i = 0; i < 10; i++) {
               // calculate the model matrix for each object and pass it to shader before drawing
               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();
    }
    
    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)
    {
        if(event->button() == Qt::LeftButton){
            m_bLeftPressed = true;
            m_lastPos = event->pos();
        }
    }
    
    void QtFunctionWidget::mouseReleaseEvent(QMouseEvent *event)
    {
        Q_UNUSED(event);
    
        m_bLeftPressed = false;
    }
    
    void QtFunctionWidget::mouseMoveEvent(QMouseEvent *event)
    {
        if (m_bLeftPressed) {
            int xpos = event->pos().x();
            int ypos = event->pos().y();
    
            int xoffset = xpos - m_lastPos.x();
            int yoffset = m_lastPos.y() - ypos;
            m_lastPos = event->pos();
            camera->processMouseMovement(xoffset, yoffset);
        }
    }
    
    void QtFunctionWidget::wheelEvent(QWheelEvent *event)
    {
        QPoint offset = event->angleDelta();
        camera->processMouseScroll(offset.y()/20.0f);
    }
    

    Camera.h

    #ifndef CAMERA_H
    #define CAMERA_H
    
    #include <QVector3D>
    #include <QMatrix4x4>
    #include <QKeyEvent>
    
    // Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
    enum Camera_Movement {
      FORWARD,
      BACKWARD,
      LEFT,
      RIGHT,
      UP,
      DOWN
    };
    
    // Default camera values
    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:
        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;
    
        QVector3D up;
        QVector3D right;
    
        //Eular Angles
        float picth;
        float yaw;
    
        //Camera options
        float movementSpeed;
        float mouseSensitivity;
        float zoom;
    
        //Keyboard multi-touch
        bool keys[1024];
    private:
        void updateCameraVectors();
        void processKeyboard(Camera_Movement direction, float deltaTime);
    };
    
    #endif // CAMERA_H
    

    Camera.cpp

    #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;
        view.lookAt(this->position, this->position + this->front, this->up);
        return view;
    }
    
    // Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
    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;
    }
    
    // Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
    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();
    }
    
    // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
    void Camera::processMouseScroll(float yoffset)
    {
      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()
    {
        // Calculate the new Front vector
        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();
        this->right = QVector3D::crossProduct(this->front, this->worldUp).normalized();
        this->up = QVector3D::crossProduct(this->right, this->front).normalized();
    }
    

    参考链接

    相关文章

      网友评论

        本文标题:基于Qt的OpenGL学习(6)—— 摄像机

        本文链接:https://www.haomeiwen.com/subject/meaujctx.html