QT+OpenGL初学实现摄像机在3D场景自由移动

简介

记录一下最近入门OpenGL的学习成果,效果如下:

开发环境

Windows10、Qt5.13.2(编译器用的是MinGW64_bit)、OpenGL3.3

项目文件结构

mainwindow:程序启动主窗口,用于响应菜单栏点击事件

myopenglwidget:自定义的openglwidget,用于显示场景

Camera.h:摄像机封装类,供其它文件调用,用于记录摄像机的坐标系及响应鼠标键盘事件

shapes.vert:用于顶点着色器,绘制物体

shapes.frag:用于片段着色器,为物体添加材质或颜色

textures文件夹存放用到的材质

源代码

Camera.h

#include <vector>
#include <QMatrix4x4>
#include <qopengl.h>
#include <math.h>

enum Camera_Movement{
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT
};

const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
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;
    float MouseSensitivity;
    float 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):
        Front(QVector3D(0.0f,0.0f,-1.0f)),
        MovementSpeed(SPEED),
        MouseSensitivity(SENSITIVITY),
        Zoom(ZOOM)
    {
        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):
        Front(QVector3D(0.0f, 0.0f, -1.0f)),
        MovementSpeed(SPEED),
        MouseSensitivity(SENSITIVITY),
        Zoom(ZOOM)
    {
        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 processKeybord(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;
    }

    void processMouseMovement(float xoffset,float yoffset,GLboolean constrainPitch=true)
    {
        xoffset *= MouseSensitivity;
        yoffset *= MouseSensitivity;

        Yaw += xoffset;
        Pitch += yoffset;

        if(constrainPitch){
            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.1415926;
        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();
    }
};

myopenglwidget

代码太多只放关键部分,详细可看文末github链接

//创建窗口
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    //启动定时器,每隔TIME_OUT_INTERVAL会刷新一下OpenGLWidget
    connect(&m_timer,&QTimer::timeout,this,&MyOpenGLWidget::on_timeout);
    m_timer.start(TIME_OUT_INTERVAL);
    setFocusPolicy(Qt::StrongFocus);
    setMouseTracking(true);

    m_camera.Position=QVector3D(0.0,0.0,6.0);
}
//下面这两个函数为mainwindow的点击事件服务,当mainwindow捕捉到相应点击事件时,调用这些函数实现OpenGLWidget的重新绘制
void MyOpenGLWidget::drawShape(MyOpenGLWidget::Shape shape)
{
    m_shape=shape;
    update();
}
void MyOpenGLWidget::setWireFrame(bool mode)
{
    makeCurrent();
    if(mode){
        glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
    }
    else{
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    }
    update();
    doneCurrent();
}
//自定义OpenGLWidget需要实现的三个函数initializeGL、resizeGL、paintGL
//用VAO[0]和VBO[0]画三角形,用VAO[0]和EBO画矩形,用VAO[1]和VBO[1]画立方体,顶点分别存放在vertices、indices、cube中
void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    glGenVertexArrays(2,VAO);
    glGenBuffers(2,VBO);
    glGenBuffers(1,&EBO);

    glBindVertexArray(VAO[0]);
    glBindBuffer(GL_ARRAY_BUFFER,VBO[0]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);

    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");
    shaderProgram.link();

    m_block_texture=new QOpenGLTexture(QImage(":/textures/container.jpg").mirrored());
    m_block_texture2=new QOpenGLTexture(QImage(":/textures/container2.png").mirrored());
    m_smile_texture=new QOpenGLTexture(QImage(":/textures/awesomeface.png").mirrored());
    // 绑定,只有在绑定完后才可以对这个shader的变量进行设置
    shaderProgram.bind();
    shaderProgram.setUniformValue("texture0",0);
    shaderProgram.setUniformValue("texture1",1);
    shaderProgram.setUniformValue("texture2",2);
    shaderProgram.setUniformValue("ratio",ratio);

    glBufferData(GL_ARRAY_BUFFER,sizeof (vertices),vertices,GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof (indices),indices,GL_STATIC_DRAW);
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)(3*sizeof (float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)(6*sizeof (float)));
    glEnableVertexAttribArray(2);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindVertexArray(0);

    //cube
    glBindVertexArray(VAO[1]);
    glBindBuffer(GL_ARRAY_BUFFER,VBO[1]);

    glBufferData(GL_ARRAY_BUFFER,sizeof (cube),cube,GL_STATIC_DRAW);
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,5*sizeof (float),(void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,5*sizeof (float),(void*)(3*sizeof (float)));
    glEnableVertexAttribArray(2);

    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindVertexArray(0);
}
void MyOpenGLWidget::resizeGL(int w, int h)
{
    Q_UNUSED(w)
    Q_UNUSED(h)
}
void MyOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 model,view,projection;
    shaderProgram.bind();
    m_block_texture->bind(2);
    m_block_texture2->bind(1);
    m_smile_texture->bind(0);

    switch (m_shape) {
    case Triangle:
        glBindVertexArray(VAO[0]);

        angle=(angle+5)%360;
        model.rotate(30,1.0f,0.0f,0.0f);
        model.rotate(angle,0.0f,1.0f,0.0f);
        view.translate(0.0,0.0,-6);
        projection.perspective(45,(float)width()/height(),0.1f,100);

        shaderProgram.setUniformValue("model",model);
        shaderProgram.setUniformValue("view",view);
        shaderProgram.setUniformValue("projection",projection);

        glDrawArrays(GL_TRIANGLES,0,3);//use VBO[0]
        break;
    case Rect:
        glBindVertexArray(VAO[0]);

        angle=(angle+5)%360;
        model.rotate(30,1.0f,0.0f,0.0f);
        model.rotate(angle,0.0f,1.0f,0.0f);
        view.translate(0.0,0.0,-6);
        projection.perspective(45,(float)width()/height(),0.1f,100);

        shaderProgram.setUniformValue("model",model);
        shaderProgram.setUniformValue("view",view);
        shaderProgram.setUniformValue("projection",projection);

        glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,&indices);//use EBO
        break;
    case Cube:
        glBindVertexArray(VAO[1]);

        angle=(angle+5)%360;
        view=m_camera.getViewMatrix();
        projection.perspective(45,(float)width()/height(),0.1f,100);

        shaderProgram.setUniformValue("view",view);
        shaderProgram.setUniformValue("projection",projection);
        foreach (auto item, cubePositions) {
            model.setToIdentity();
            model.translate(item);
            model.rotate(30,1.0f,0.0f,0.0f);
            model.rotate(angle,0.0f,1.0f,0.0f);
            shaderProgram.setUniformValue("model",model);
            glDrawArrays(GL_TRIANGLES,0,36);//use VBO[1]
        }
        break;
    default:
        break;
    }
}
//后面还有一些鼠标键盘响应事件用于控制摄像机的移动,它们均调用Camera类的方法来处理

mainwindow

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

//以下是菜单栏的点击响应函数
void MainWindow::on_actiontriangle_triggered()
{
    ui->openGLWidget->drawShape(MyOpenGLWidget::Triangle);
}

void MainWindow::on_actionWireFrame_triggered()
{
    ui->openGLWidget->setWireFrame(true);
}

void MainWindow::on_actionnonWireFrame_triggered()
{
    ui->openGLWidget->setWireFrame(false);
}

void MainWindow::on_actionclear_triggered()
{
    ui->openGLWidget->drawShape(MyOpenGLWidget::None);
}

void MainWindow::on_actionrect_triggered()
{
    ui->openGLWidget->drawShape(MyOpenGLWidget::Rect);
}

void MainWindow::on_actioncube_triggered()
{
    ui->openGLWidget->drawShape(MyOpenGLWidget::Cube);
}

顶点着色器(shapes.vert)

#version 330 core
layout(location=0)in vec3 aPos;
layout(location=1)in vec3 aColor;
layout(location=2)in vec2 aTexCord;
out vec3 ourColor;
out vec2 TexCord;
//uniform mat4 RotationMatrix;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main(){
    gl_Position=projection*view*model*vec4(aPos.x,aPos.y,aPos.z,1.0f);
    ourColor=aColor;
    TexCord=aTexCord;
}

片段着色器(shapes.frag)

#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCord;
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float ratio;

void main(){
    FragColor=mix(texture(texture0,TexCord),texture(texture1,TexCord),ratio);
}

完整代码在:

https://github.com/FonlinGH/TestOpenGL

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Qt 是一款跨平台应用程序开发框架,它提供了丰富的功能和工具来简化应用程序的开发。OpenGL 是一种图形渲染库,用于创建高性能的2D和3D图形效果。那么,QtOpenGL 的结合能够为开发者提供强大的图形处理能力和丰富的用户界面设计功能。 在学习使用 QtOpenGL 进行应用程序开发时,书籍是非常有帮助的资源。下面是一些关于 QtOpenGL 的书籍推荐: 1.《Qt5 权威指南》:这本书全面介绍了 Qt 的各个方面,包括 Qt 的基础知识、GUI 编程、网络编程、数据库编程等内容,对初学者来说非常友好。同时,书中还有一章专门介绍了如何在 Qt 中使用 OpenGL 进行图形绘制。 2.《Qt5 与 OpenGL 高级编程指南》:这本书着重介绍了 Qt5 和 OpenGL 的结合应用,涵盖了 OpenGL 基础知识、渲染管线、光照、纹理、阴影等高级图形技术。它适合有一定编程经验且对图形编程有兴趣的开发者。 3.《OpenGL编程指南(第九版)》:虽然这本书不是专门讲解 Qt,但是它是学习 OpenGL 必备的经典教材之一。它详细介绍了 OpenGL 的基础知识和相关概念,包括顶点缓冲对象、渲染缓冲区、着色器编程等内容。通过学习这本书,开发者可以更加深入地理解 OpenGL 的工作原理。 以上是一些关于 QtOpenGL 的书籍推荐,选择适合自己水平和需求的书籍进行学习,可以帮助我们更好地掌握 QtOpenGL 的开发技巧,进而开发出功能丰富且高性能的应用程序。 ### 回答2: Qt是一种跨平台的应用程序开发框架,它提供了丰富的GUI界面设计工具和功能强大的库,可以帮助开发者轻松地开发出高质量的图形界面应用程序。而OpenGL是一种专门用于图形渲染的API,可以实现高性能、高质量的图形渲染效果。 在Qt中使用OpenGL可以帮助开发者更好地利用硬件加速来实现图形渲染,提高程序的性能和效果。因此,学习QtOpenGL的结合使用对于想要开发高质量图形应用程序的开发者来说是非常有价值的。 关于QtOpenGL的书籍,市面上有很多优秀的选择。其中,一本非常经典的书籍是《Qt5与OpenGL开发实战指南》。这本书适合有Qt基础并对OpenGL感兴趣的开发者。书中介绍了使用QtOpenGL进行图形编程的基本原理和技巧,并通过丰富的实例演示了如何使用QtOpenGL实现各种图形效果和交互特性。 除了这本书之外,还有一些其他的书籍也值得推荐。例如,《OpenGL编程指南》是一本经典的OpenGL入门书籍,可以帮助开发者深入理解OpenGL的基本原理和使用方法。《Qt5开发及实例精解》是一本全面介绍Qt开发的书籍,其中也包含了一些关于QtOpenGL结合使用的内容。 总之,选择适合自己的QtOpenGL书籍,可以帮助开发者更好地掌握QtOpenGL的基本知识和技术,从而开发出更加高效、高质量的图形应用程序。 ### 回答3: Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的图形界面和功能库,可以帮助开发者快速地构建高质量的应用程序。OpenGL则是一个用于图形渲染的开放标准,通过与Qt结合使用,可以实现更高级的图形效果和游戏开发。 对于想深入学习QtOpenGL的开发者来说,有一些经典的书籍可以提供帮助。首先推荐《Qt编程开发实战》一书,该书详细介绍了Qt的基本概念和常用功能,让读者能够快速入门。接下来,可以阅读《QtOpenGL开发指南》,该书介绍了如何使用QtOpenGL创建高级图形应用程序,并深入解析了底层原理和技术细节。此外,《OpenGL超级宝典》是一本非常经典的OpenGL入门书籍,其中包含了许多实用的代码示例和案例讲解,对于理解OpenGL的基本原理和使用方法非常有帮助。 除了这些书籍,互联网上也有许多免费的教程和博客可以供开发者参考,例如《Qt官方文档》和《OpenGL教程》等。此外,参加一些相关的培训课程或者在线教育平台提供的课程,也是学习QtOpenGL的有效途径。最重要的是,实践是学习的关键,通过动手实践一些小项目,可以帮助开发者更好地理解和应用所学知识。 总的来说,QtOpenGL是非常强大的开发工具,在学习过程中可以参考一些经典的书籍和在线资源,结合实践经验,迅速掌握它们的使用方法和技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值