Qt实现OpenGL的经典教程⑦——摄像机

本帖将参考LearnOpenGL这一经典教程,使用Qt的原生环境完成教程中所提的所有流程,并尽量和原教程保持一致,如有错误,欢迎评论!

为了方便大家参考,我将项目分享至了gitee,并实时进行更行:Qt实现OpenGL的经典教程: (gitee.com)

教程中文网站:LearnOpenGLCN 

教程原网站:LearnOpenGL

Qt版本:6.7

操作系统:Windows10

旋转摄像机

对摄像机的操控这里需要用到LookAt矩阵,好在Qt的QMatrix4x4库中也对它进行了封装,这里我们对上一篇中的代码进行修改,只修改PaintGL中的内容,调整摄像机矩阵View,具体代码如下:

void MyOpenGLWidget::paintGL()
{
    //由于继承了QOpenGLFunctions,可以直接使用OpenGL中的函数
    // render
    // ------
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓冲

    // bind Texture
    glActiveTexture(GL_TEXTURE0);
    _texture->bind();
    glActiveTexture(GL_TEXTURE1);
    _texture1->bind();

    QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 projection;

    float radius = 10.0f;
    float camX = sin(QTime::currentTime().msecsSinceStartOfDay()/1000.0f) * radius;
    float camZ = cos(QTime::currentTime().msecsSinceStartOfDay()/1000.0f) * radius;
    view.lookAt(QVector3D(camX, 0.0f, camZ),
                QVector3D(0.0f, 0.0f, 0.0f),
                QVector3D(0.0f, 1.0f, 0.0f));
    projection.perspective(45.0f,
                           float(this->width())/float(this->height()),
                           0.1f,
                           100.0f);


    // update shader uniform
    _shaderProgram->bind();
    _shaderProgram->setUniformValue("view", view);
    _shaderProgram->setUniformValue("projection", projection);

    // render the triangle
    _vao->bind();
    for(int i = 0; i < _cubePositions.size(); ++i){

        model = QMatrix4x4();
        model.translate(_cubePositions[i]);
        model.rotate(20.0f * i, 1.0f, 0.3f, 0.5f);
        _shaderProgram->setUniformValue("model", model);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }
}

我们主要使用了LookAt方法来修改定义的View举证,lookat三个参数分别是摄像机位置,目标位置,以及上向量。通过这种方法,便可以自由地控制摄像机视角。我们通过sin和cos来实现一个摄像机圆周运动,点击运行,可得到如下结果:

自由移动

现在,让我们添加一些监控键盘与鼠标输入来控制摄像机的位置。在Qt中捕捉键盘输入需要重写继承的keyPressEvent函数,在MyOpenGLWidget.h的类定义中添加以下代码:

protected:
    void initializeGL()override;
    void resizeGL(int width, int height)override;
    void paintGL()override;

    //重写键盘事件
    void keyPressEvent(QKeyEvent *event)override;

同时为了计算帧生成时间和控制相机位置,我们在MyOpenGLWidget.h的类定义中同时添加以下代码:

private:
  
    //帧处理时间
    int _lastMescCount = 0;
    int _deltaTime = 0;

    //摄像机相关
    QVector3D _cameraPos = {0.0f, 0.0f, 3.0f};
    QVector3D _cameraFront = {0.0f, 0.0f, -1.0f};
    QVector3D _cameraUp = {0.0f, 1.0f, 0.0f};
    float _moveSpeed = 1.0f;

我们在keyPressEvent定义中更行相机位置信息:

void MyOpenGLWidget::keyPressEvent(QKeyEvent *event){
    float moveSpeed = static_cast<float>(_moveSpeed * _deltaTime);
    if(event->key() == Qt::Key_W){
        _cameraPos += moveSpeed * _cameraFront;
    }
    if(event->key() == Qt::Key_S){
        _cameraPos -= moveSpeed * _cameraFront;
    }
    if(event->key() == Qt::Key_A){
        _cameraPos -= QVector3D::crossProduct(_cameraFront, _cameraUp).normalized() * moveSpeed;
    }
    if(event->key() == Qt::Key_D){
        _cameraPos += QVector3D::crossProduct(_cameraFront, _cameraUp).normalized() * moveSpeed;
    }
    //执行默认事件,向下传递
    QOpenGLWidget::keyPressEvent(event);
}

这里注意以下,我们要开启MyOpenGLWidget对键盘事件的捕捉,在其构造函数中添加以下一行代码:

    //开启键盘事件捕捉
    this->grabKeyboard();

最后,我们在PaintGL中更新帧生成时间和生成用于控制相机位置的view矩阵:


    //更新帧时间,由于定时器60fps,所以这里的_deltaTime为17上下
    _deltaTime = QTime::currentTime().msecsSinceStartOfDay() - _lastMescCount;
    _lastMescCount = QTime::currentTime().msecsSinceStartOfDay();

    QMatrix4x4 model;
    QMatrix4x4 projection;
    QMatrix4x4 view;

    view.lookAt(_cameraPos, _cameraPos + _cameraFront, _cameraUp);

点击运行,便得到一个可以键盘控制的相机:

鼠标控制视角

接着,我们捕捉鼠标输入,对摄像机的视角朝向进行设置,同理,我们重写鼠标的移动函数MouseMoveEvent

同时在MyOpengGLWidget.h中添加以下内容

    float _yaw = -90.0f;
    float _pitch = 0.0f;
    float _sensitivity = 0.1f;
    
    //屏幕中心点
    QPoint _lastMousePos = rect().center();

这是它的头文件内容是这样的:

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H

#include <QWidget>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>


class MyOpenGLWidget : public QOpenGLWidget, public QOpenGLFunctions_3_3_Core
{
    Q_OBJECT

public:
    explicit MyOpenGLWidget();
    ~MyOpenGLWidget();

protected:
    void initializeGL()override;
    void resizeGL(int width, int height)override;
    void paintGL()override;

    //重写键盘事件
    void keyPressEvent(QKeyEvent *event)override;
    //重写鼠标事件
    void mouseMoveEvent(QMouseEvent *event)override;


private:
    QOpenGLShaderProgram *_shaderProgram;
    QOpenGLTexture *_texture;
    QOpenGLTexture *_texture1;
    QOpenGLVertexArrayObject *_vao;
    QOpenGLBuffer *_vbo;
    QOpenGLBuffer *_ebo;

    QList<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)
    };

    //帧处理时间
    int _lastMescCount = 0;
    float _deltaTime = 0;

    //摄像机相关
    QVector3D _cameraPos = {0.0f, 0.0f, 3.0f};
    QVector3D _cameraFront = {0.0f, 0.0f, -1.0f};
    QVector3D _cameraUp = {0.0f, 1.0f, 0.0f};
    float _moveSpeed = 5.0f;
    float _yaw = -90.0f;
    float _pitch = 0.0f;
    float _sensitivity = 0.1f;
    
    //屏幕中心点
    QPoint _lastMousePos = rect().center();


};

#endif // MYOPENGLWIDGET_H

我们在mouseMoveEvent的定义中进行以下实现:

void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event){
    float xOffset = event->pos().x() - _lastMousePos.x();
    float yOffset =  - event->pos().y() + _lastMousePos.y();
    // 获取当前widget的中心点坐标

    // 将鼠标设置到中心点
    QCursor::setPos(mapToGlobal(_lastMousePos));

    xOffset *= _sensitivity;
    yOffset *= _sensitivity;

    _yaw += xOffset;
    _pitch += yOffset;

    // make sure that when pitch is out of bounds, screen doesn't get flipped
    if (_pitch > 89.0f)
        _pitch = 89.0f;
    if (_pitch < -89.0f)
        _pitch = -89.0f;

    _cameraFront.setX(cos(qDegreesToRadians(_yaw)) * cos(qDegreesToRadians(_pitch)));
    _cameraFront.setY(sin(qDegreesToRadians(_pitch)));
    _cameraFront.setZ(sin(qDegreesToRadians(_yaw)) * cos(qDegreesToRadians(_pitch)));
    _cameraFront.normalize();
}

同时开启鼠标跟踪:

MyOpenGLWidget::MyOpenGLWidget()
{
    //设置上下文属性
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 3); //OpenGL 版本号3.3
    format.setProfile(QSurfaceFormat::CoreProfile);// 核心模式
    this->setFormat(format);
    //开启键盘事件捕捉
    this->grabKeyboard();
    //开启捕捉鼠标事件
    this->grabMouse();
    // 启用鼠标追踪
    this->setMouseTracking(true);
 }

点击运行,便得到了一个FPS视角的3D界面了!

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Qt4.8是一个基于C++的开发框架,它提供了一种简化和加速应用程序开发的方式。OpenGL是一种跨平台的图形渲染API,它可以用于创建高性能的3D图形应用程序。Qt4.8中集成了OpenGL模块,使得开发者可以使用Qt的API来创建基于OpenGL的应用程序。 关于Qt4.8的OpenGL教程,可以从以下几个方面进行介绍。 首先,Qt4.8提供了一些类和函数来简化OpenGL的使用。比如,可以使用QGLWidget类来创建一个OpenGL窗口,可以使用QGLFunctions类来访问OpenGL的函数。 其次,Qt4.8提供了一些示例程序和文档来帮助开发者学习和理解OpenGL的使用。在Qt的安装目录下,可以找到一些示例程序,其中包括了一些使用OpenGL的示例。此外,Qt的官方文档中也提供了关于OpenGL的详细介绍和使用方法。 此外,还有一些第三方的教程和示例程序可以帮助开发者学习Qt4.8中的OpenGL模块。例如,可以在网络上搜索一些Qt4.8的OpenGL教程,或者在一些开源项目中找到使用了Qt4.8和OpenGL的示例程序。 总的来说,学习Qt4.8中的OpenGL模块需要掌握基本的Qt编程知识和OpenGL的基础知识。通过阅读文档、学习示例程序和参考第三方教程,开发者可以更好地理解和应用Qt4.8中的OpenGL模块,从而创建出高性能的图形应用程序。 ### 回答2: QT 4.8是一种流行的跨平台应用程序开发框架,而OpenGL是一种强大的图形渲染API。由于QT 4.8具有强大的界面设计和事件处理功能,结合OpenGL可以实现各种复杂的图形和动画效果。 QT 4.8中的OpenGL教程可以帮助开发人员了解如何使用OpenGLQT程序中创建2D和3D图形教程通常从基础开始,介绍OpenGL的基本概念和原理,如顶点缓冲对象、着色器、纹理等等。然后,教程会演示如何在QT程序中集成OpenGL,包括创建一个OpenGL窗口并进行初始化设置。 教程还会介绍如何在QT中使用OpenGL进行各种图形渲染,如绘制基本形状、绘制立方体和球体、应用光照和阴影效果等等。此外,教程还会展示如何处理用户输入和事件,以及如何使图形交互起来,例如通过鼠标或键盘进行旋转、缩放和平移等操作。 值得一提的是,由于QT 4.8使用了信号和槽机制,开发人员可以很方便地与OpenGL渲染循环进行交互,并实时更新图形显示。此外,QT还提供了一些方便的辅助工具和类,例如QGLWidget和QOpenGLFunctions等,可以简化开发过程。 总之,QT 4.8的OpenGL教程可以帮助开发人员快速入门并掌握在QT应用程序中使用OpenGL进行图形渲染的技巧和方法。通过学习这些教程,开发人员可以创建出更具吸引力和互动性的应用程序,从而丰富用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值