基于QT使用OpenGL,加载obj模型,进行鼠标交互

这篇具有很好参考价值的文章主要介绍了基于QT使用OpenGL,加载obj模型,进行鼠标交互。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

功能分析(需求分析)

  1. 基于QT平台,使用OpenGL进行obj文件加载显示;
  2. 使用鼠标对场景进行缩放、移动、旋转交互;

技术点分析

OpenGL

  OpenGL是基于C的,学习曲线比较抖,但是总的来说就是下面一幅图,
qt的opengl导入模型,QT/OpenGL,qt,计算机外设,交互

  用语言简单的描述(个人理解,可能不太准确)是把cpu里内存里的3D数据,传输到显卡的内存里,以及如何转换成2D平面上像素点显示(也就是矩阵变换,在好多开源的框架里都进行了进一步封装,形成了渲染器、场景、相机等)。这个数据是顶点坐标、颜色等,传输跟送快递一样,除了要有数据本身之外,还要有其他信息,也就是要有个约定,到显卡拿到数据之后怎么解析。
  有个网站比较有名learnopengl,可以对着学一遍,在网上找资料的时候,需要注意是立即渲染模式,还是可编程渲染管线模式,现在官方推荐是使用可编程渲染管线模式开发。

立即渲染模式

glBegin(GL_TRIANGLES)
glTranslatef(1,2,3);
// 其他操作
// glVertex*() 设置顶点坐标
// glColor*() 设置当前颜色
// glIndex*() 设置当前颜色表
// glNormal*() 设置法向坐标
// glEvalCoord*() 产生坐标
// glTexCoord*() 设置纹理坐标
// glEdgeFlag*() 控制边界绘制
// glMaterial*() 设置材质
glEnd()

可编程渲染管线模式

1、创建VBO顶点数据对象
	GLuint VBO;
	glGenBuffers(1,&VBO);
2、VBO与显卡缓存绑定
	glBindBuffer(GL_ARRAY_BUFFER, VBO)
3、绑定数据缓存对象
	glBufferData(GL_ARRAY_BUFFER,sizeof(vertexs),vertex,)
4、数据格式
	glVertexAttribPoint(0,3,GL_FLOAT,)
5、启用/绘制
	glEnableVertexAttribArray(0)
	glDrawArrays(GL_TRIANGLES, 0, 10);
6、最后关闭
	glBindBuffer(GL_ARRAY_BUFFER,0)
	glBindVertexArray(0);

  以上两段代码只是展示两种模式区别,很明显立即渲染模式容易理解,但是性能有限制,可编程渲染管线模式理解和使用门槛高,但更能接触底层。

QOpenGLWidget

  QT对OpenGL进行了封装,提供了QOpenGLWidget类,只需要对其继承,在initialzeGL()resizeGL()paintGL()逻辑下进行业务实现,包括VBO、VAO的创建与绑定,着色器程序的编译与链接等。

派生类 glwidget逻辑

  先简单介绍派生类的写法,当然为了方便扩展和使用,抽象出了相机类、渲染器类、物体类,完整的工程代码资源可以下载参考,具体的可以根据自己要实现的功能进行编写。

glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMouseEvent>
#include <QTimer>
#include <QQuaternion>

#include <vector>
#include <QKeyEvent>

#include "utils/Common.h"
#include "genericRender.h"
#include "grid.h"
#include "coorsystem.h"
#include "camera.h"

class glwidget :public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    glwidget(QWidget * parent = nullptr);
    //鼠标交互事件重写
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void wheelEvent(QWheelEvent *event) override;
    //键盘交互
    void keyReleaseEvent(QKeyEvent *event) override; //按键释放事件
    void keyPressEvent(QKeyEvent *event) override;   //按键按下事件
    void slotTimeOut();
protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();
public:
    GenericRender m_render;//渲染器
    Camera m_camera;//相机
    grid m_grid;//网格平面xy
    coorSystem m_coordsys;//坐标系
    QTimer tm_;
public:
    QList<int> keys;
    QTimer* keyRespondTimer;	//头文件中添加成员
    static int mouse_button;
    static int modifier_key;
public:
    //鼠标变量
    static double mouse_pos_x_old;
    static double mouse_pos_y_old;
    bool changeview_ = false;            //改变视角的标志位
    float fov = 2.0f;          //视野范围
};
#endif // GLWIDGET_H
glwidget.cpp
#include "glwidget.h"
#include "box.h"
int glwidget::mouse_button = -1;
int glwidget::modifier_key = 0;
double glwidget::mouse_pos_x_old = 0;
double glwidget::mouse_pos_y_old = 0;
glwidget::glwidget(QWidget * parent):QOpenGLWidget(parent)
{
    keyRespondTimer = new QTimer(this);	//构造函数中创建定时器对象,并连接信号槽
    connect(keyRespondTimer, &QTimer::timeout, this, &glwidget::slotTimeOut);
    setFocusPolicy(Qt::StrongFocus);//否则进入不了键盘事件监听
}

void glwidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
    m_render.init();
    m_camera.setMatPro(fov);
    QStringList renderfiles={":/obj/data/Cayman_GT.obj", ":/obj/data/bunny_10k.obj"};
    for(QString file : renderfiles){
        Box* b1 = new Box();
        b1->load(file);
        m_render.addBox(b1);
    }
    m_grid.initize();
    m_coordsys.initize();
}

void glwidget::resizeGL(int w, int h)
{
    m_camera.setRatio((float)width() / (float)height());
}

void glwidget::paintGL()
{
    glClearColor(1.0f, 1.0f, 1.0f, 0.1f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPolygonMode(GL_FRONT, GL_LINE);
    QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
    QMatrix4x4 mMatrix;
    m_camera.setMatPro(fov);
    m_grid.paint(f,m_camera.getMatPro(),m_camera.getMatView(),mMatrix);
    m_coordsys.paint(f,m_camera.getMatPro(),m_camera.getMatView(),mMatrix);
    m_render.render(f,m_camera,mMatrix);
}

void glwidget::mousePressEvent(QMouseEvent *event){
    mouse_pos_x_old = event->pos().x();
    mouse_pos_y_old = event->pos().y();
    mouse_button = event->button();
}

void glwidget::mouseMoveEvent(QMouseEvent *event){
    int x = event->pos().x();
    int y = event->pos().y();
    double w = width(), h = height();
    double d_x = (mouse_pos_x_old - x)/w;
    double d_y = (mouse_pos_y_old - y)/h;
    if (mouse_button == Qt::LeftButton)
    {
        foreach (int key, keys) {
            switch (key) {
            case Qt::Key_Control:
                m_camera.rotateY(-d_x*180*5);
                m_camera.rotateX(-d_y*180*5);
                break;
            case Qt::Key_Shift:
                m_camera.move(5*d_x, 5*d_y, 0);
                break;
            default:
                break;
            }
        }
    }
    mouse_pos_x_old = x;
    mouse_pos_y_old = y;
    this->repaint();
}

void glwidget::mouseReleaseEvent(QMouseEvent *event){  //鼠标左键松开禁止改变相机视角
    changeview_ = false;
}

void glwidget::wheelEvent(QWheelEvent *event){
    if (event->delta() > 0)
        fov-=2.0f;
    else
        fov+=2.0f;
    if (fov<0.50f)
        fov = 0.5f;
    if (fov>=200.f)
        fov = 200.0;
    this->repaint();
}

void glwidget::keyReleaseEvent(QKeyEvent *event)
{
    if(!event->isAutoRepeat())  //判断如果不是长按时自动触发的释放,就将key值从容器中删除
        keys.removeAll(event->key());
    if(keys.isEmpty()) //容器空了,关闭定时器
        keyRespondTimer->stop();
}

void glwidget::keyPressEvent(QKeyEvent *event)
{
    if(!event->isAutoRepeat())  //判断如果不是长按时自动触发的按下,就将key值加入容器
        keys.append(event->key());
    if(!keyRespondTimer->isActive()) //如果定时器不在运行,就启动一下
        keyRespondTimer->start(4);
}

void glwidget::slotTimeOut(){
    foreach (int key, keys) {
        switch (key) {
        case Qt::Key_D:
            break;
        case Qt::Key_Shift:
            modifier_key = Qt::Key_Shift;
            break;
        default:
            break;
        }
    }
}

鼠标交互功能

  交互的功能实现是重载QOpenGLWidget的鼠标事件函数,修改相应的矩阵,也就对应最上面的相机变化、灯光变化等;

obj格式介绍

  OBJ文件(.obj)包含有关3D对象的几何体的信息,下面是一个长方体的obj格式文件;

#
# Object file
#
mtllib Cube3x3x10.mtl
# Cube3x3x10\实体
v 0 0 0
v 3.00000002607703 0 0
v 3.00000002607703 0 9.99999977648258
v 0 0 9.99999977648258
v 3.00000002607703 3.00000002607703 9.99999977648258
v 0 3.00000002607703 9.99999977648258
v 0 0 9.99999977648258
v 3.00000002607703 0 9.99999977648258
v 0 3.00000002607703 9.99999977648258
v 0 3.00000002607703 0
v 0 0 0
v 0 0 9.99999977648258
v 0 3.00000002607703 0
v 3.00000002607703 3.00000002607703 0
v 3.00000002607703 0 0
v 0 0 0
v 3.00000002607703 3.00000002607703 0
v 3.00000002607703 3.00000002607703 9.99999977648258
v 3.00000002607703 0 9.99999977648258
v 3.00000002607703 0 0
v 3.00000002607703 3.00000002607703 9.99999977648258
v 3.00000002607703 3.00000002607703 0
v 0 3.00000002607703 0
v 0 3.00000002607703 9.99999977648258
vn 0 -1 0
vn 0 -1 0
vn 0 -1 0
vn 0 -1 0
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn -1 0 0
vn -1 0 0
vn -1 0 0
vn -1 0 0
vn 0 0 -1
vn 0 0 -1
vn 0 0 -1
vn 0 0 -1
vn 1 0 0
vn 1 0 0
vn 1 0 0
vn 1 0 0
vn 0 1 0
vn 0 1 0
vn 0 1 0
vn 0 1 0
vt 0 0
vt 0 0.00300000002607703
vt 0.00999999977648258 0.00300000002607703
vt 0.00999999977648258 0
vt 0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0
vt 0.00150000001303852 0
vt 0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0
vt 0.00499999988824129 0
vt 0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0.00300000002607703
vt -0.00150000001303852 0
vt 0.00150000001303852 0
vt 0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0.00300000002607703
vt -0.00499999988824129 0
vt 0.00499999988824129 0
vt 0.00999999977648258 0.00300000002607703
vt 0 0.00300000002607703
vt 0 0
vt 0.00999999977648258 0
o Cube3x3x10\实体
s off
# face 0
f 2/2/2 3/3/3 1/1/1
f 1/1/1 3/3/3 4/4/4
# face 1
f 5/5/5 6/6/6 8/8/8
f 8/8/8 6/6/6 7/7/7
# face 2
f 9/9/9 10/10/10 12/12/12
f 12/12/12 10/10/10 11/11/11
# face 3
f 13/13/13 14/14/14 16/16/16
f 16/16/16 14/14/14 15/15/15
# face 4
f 17/17/17 18/18/18 20/20/20
f 20/20/20 18/18/18 19/19/19
# face 5
f 21/21/21 22/22/22 24/24/24
f 24/24/24 22/22/22 23/23/23

  其中

  • mtllib Cube3x3x10.mtl   表示引用的材质文件的文件名
  • v 0 0 0          表示一个点的xyz坐标,使用空格隔开
  • vn 0 -1 0        表示一个点的法向量,使用空格隔开
  • vt 0.09 0        表示uv纹理坐标,使用空格隔开
  • f 2/2/2 3/3/3 1/1/1    表示一个面, 2/2/2依次为顶点索引,纹理坐标索引,法向索引,因为是三角面片,所以是三组,也有超多三个点的,自己造轮子的时候要注意
  • o Cube      表示指定了模型名称为Cube
  • s off       表示关闭光滑组

void ObjLoader::load(QString filename)
{
    // 打开文件
    QFile file(filename);
    if(!file.open(QIODevice::ReadOnly)){
        qDebug()<<"[Error] fail to open file: "<< filename;
        return;
    }
    // 读取文件
    QTextStream ts(&file);
    // 临时存储
    QVector<QVector3D> v;
    QVector<QVector3D> vn;
    QVector<QVector3D> vt;
    QVector<QStringList> str_faces;
    QVector<Face> faces;
    while(!ts.atEnd()){
        QStringList list = ts.readLine().split(QRegExp("(\\s+)"));
        list.removeAll(" ");
        if(list.size() == 0 )
            break;
        if(list[0] == "v")
            v.push_back(QVector3D(list[1].toFloat(),list[2].toFloat(),list[3].toFloat()));
        if(list[0] == "vn")
            vn.push_back(QVector3D(list[1].toFloat(),list[2].toFloat(),list[3].toFloat()));
        if(list[0] == "vt")
            vt.push_back(QVector3D(list[1].toFloat(),list[2].toFloat(),0));
        if(list[0] == "f")
            str_faces.push_back(list);
    }

    for( int i =0; i< str_faces.size(); i++)
    {
        Face face;
        for( int j = 1; j<=3; j++)  //obj中顶点索引是从1开始
        {
            QStringList list = str_faces[i][j].split("/");
            face.v[j-1] = list[0].toInt() -1;
            face.t[j-1] = list[1].toInt() -1;
            face.n[j-1] = list[2].toInt() -1;
        }
        faces.push_back(face);
    }

    for( int i = 0; i<faces.size();i++)
    {
        QVector3D a,b,c,na,nb,nc;
        a = v[faces[i].v[0]];
        b = v[faces[i].v[1]];
        c = v[faces[i].v[2]];
        na = vn[faces[i].n[0]];
        nb = vn[faces[i].n[1]];
        nc = vn[faces[i].n[2]];

        mv.push_back(a.x());
        mv.push_back(a.y());
        mv.push_back(a.z());
        mv.push_back(b.x());
        mv.push_back(b.y());
        mv.push_back(b.z());
        mv.push_back(c.x());
        mv.push_back(c.y());
        mv.push_back(c.z());
        
        mn.push_back(na.x());
        mn.push_back(na.y());
        mn.push_back(na.z());
        mn.push_back(nb.x());
        mn.push_back(nb.y());
        mn.push_back(nb.z());
        mn.push_back(nc.x());
        mn.push_back(nc.y());
        mn.push_back(nc.z());
    }
    file.close();
    std::cout<< filename.toStdString() <<" id succeded!\t model_size:"<<mv.size()/3<<std::endl;
}

效果

 暂时还不会弄视频或者动态图,先看个静态效果吧

bunny

qt的opengl导入模型,QT/OpenGL,qt,计算机外设,交互

Cayman_GT

qt的opengl导入模型,QT/OpenGL,qt,计算机外设,交互文章来源地址https://www.toymoban.com/news/detail-761676.html

到了这里,关于基于QT使用OpenGL,加载obj模型,进行鼠标交互的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何使用 ThreeJs 以 glTF、FBX 和 OBJ 文件格式加载 3D 模型,使用 ThreeJS 加载和显示带有纹理的 3D 模型

    在本文中,我展示了如何使用 ThreeJS 在 Web 视图中加载 3D 模型。Three.js 是一个跨浏览器的 JavaScript 库和应用程序编程接口,用于使用 WebGL 在 Web 浏览器中创建和显示动画 3D 计算机图形。加载不完整的原因有很多,例如纹理和材质渲染不正确。 创建场景 渲染场景 动画立方体

    2023年04月08日
    浏览(50)
  • 使用Open3D进行OBJ模型三维可视化

    使用Open3D进行OBJ模型三维可视化 在三维图像处理领域,OBJ文件是一种常见的三维模型格式,而Open3D则是一个强大的开源3D计算机视觉库。本文将介绍如何使用Open3D对OBJ文件进行可视化。 首先,需要安装Open3D库。使用pip install即可: 接下来,我们可以使用Open3D提供的read_triang

    2024年02月06日
    浏览(68)
  • Qt使用OpenGL进行多线程离屏渲染

    基于Qt Widgets的Qt程序,控件的刷新默认状况下都是在UI线程中依次进行的,换言之,各个控件的QWidget::paintEvent方法会在UI线程中串行地被调用。若是某个控件的paintEvent很是耗时(等待数据时间+CPU处理时间+GPU渲染时间),会致使刷新帧率降低,界面的响应速度变慢。 假如这个

    2024年02月02日
    浏览(43)
  • 计算机图形学实验四 OpenGL的鼠标交互绘制

    一、实验目的 1.掌握OpenGL的鼠标按钮响应函数。 2.掌握OpenGL的鼠标移动响应函数。 3.进一步巩固OpenGL的基本图元绘制基础。 二、实验内容 1.鼠标画草图——实现鼠标点到哪,线就画到哪。 思路: 1)在主程序注册鼠标响应和鼠标移动子函数: glutMouseFunc(mymouse); glutMotionFunc(mym

    2023年04月20日
    浏览(113)
  • threejs加载.Fbx .OBJ 3D模型文件

    在threejs官网下载threejs的文件,可以选择直接下载,也可以跳转到GitHub拉取 拉取下来的完整文件就是这个样子 拉取成功后我们在本地安装启动服务,这样就能很快速的查看threejs的各种例子了 可以先看看官网里的例子,你想要的东西官方里面都有 后期在开发的时候需要用到b

    2023年04月08日
    浏览(41)
  • 【[Qt]基于QChartView开发的图表显示控件,支持实时显示,动态更新,支持鼠标交互等操作】

    十字线和显示坐标实现 在.h文件中定义十字线lineitem变量和坐标textitem变量 在Cpp文件中初始化 然后定义鼠标事件,在鼠标进入时显示,移出时隐藏,移动时显示。 其他实现请参考具体代码 ChartDrawer.h文件 ChartDrawer.cpp 文件 具体使用代码如下 1、初始化类对象,并加入界面布局

    2023年04月23日
    浏览(41)
  • 【计算机图形学 】扫描线多边形填充算法 | OpenGL+鼠标交互

    传送门 实现多边形扫描线填充算法,并和鼠标进行交互。 具体原理略过,会贴上完整代码,可直接运行。 环境: vs2019,OpenGL的库(可以搜索如何用vs使用OpenGL的库,可以使用vs自带的插件或者其他方法,很方便) 要点: 1.NET和AET的创建,改动 2.改变鼠标点击和鼠标拖拽的响应

    2023年04月08日
    浏览(74)
  • Unity运行时程序动态加载外部.fbx.obj模型文件

    项目中有用到这个需求,为实现Unity程序运行状态下,从程序外部动态加载fbx或obj模型,所以研究了一下,目前TriLib比较靠谱,好用,多平台适用。会提供 下载插件地址。 unity运行时加载fbx 使用的插件 下载插件地址 将Trilib压缩包内容解压到Unity工程Assets文件夹下,返回工程等

    2024年02月16日
    浏览(44)
  • Three.js加载外部glb,fbx,gltf,obj 模型文件

    vue3使用Three.js加载外部模型文件 1.安装Three.js 2.新建一个renderModel.js 用于处理Three.js相关逻辑 3.modelPage.vue 中使用页面 6.效果图:

    2024年02月15日
    浏览(44)
  • c++ QT opengl鼠标控制平移、缩放、绕点旋转

    坐标系固定在左下角 坐标系和正方形 一起旋转,但不平移与缩放 鼠标左键平移正方形,右键旋转,滚轮缩放(放大与缩小) 编写绘制正方形与坐标系函数 在OpenGL窗口界面绘制 实现鼠标左键平移移动,右键旋转,滚轮缩放(放大与缩小) 设置正方形的旋转点 ,以及坐标系

    2024年02月06日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包