QT+OpenGL鼠标操作和模型控制

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

QT+OpenGL鼠标操作和模型控制

本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主

鼠标拾取

  • 需要将世界坐标转换为视口坐标
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data);

理论有点小复杂

Mouse Picking with Ray Casting - Anton’s OpenGL 4 Tutorials (antongerdelan.net) 参考这篇文章

这部分不懂的话,暂时是没关系的,可以接着往下看代码,然后去看我的项目。

光线追踪法 从鼠标投射 3D 射线, 通过摄像机,进入场景,然后检查该光线是否与某个对象相交。

QT+OpenGL鼠标操作和模型控制,QT+OpenGL,qt

从鼠标计算射线
第 0 步:2D 视口坐标

range [0:width, height:0]

我们从鼠标光标坐标开始。这些是 2d,并且在视口坐标系中。首先我们需要获取鼠标 x,y 像素 坐标。

如果是QT的话,可以直接使用QT的mousePressEvent的evnet->pos();

这给了我们一个 x 在 0:width 和 y 从height:0 开始。请记住,0 位于此处的屏幕顶部,因此 y 轴方向与其他坐标系中的方向相反。

第 1 步:3d归一化设备坐标

range [-1:1, -1:1, -1:1]

下一步是将其转换为 3D 规范化设备坐标。 这应该在 x [-1:1] y [-1:1] 和 z [-1:1] 的范围内。我们有一个 x 和 y已经,所以我们缩放它们的范围,并反转y的方向。

float x = (2.0f * mouse_x) / width - 1.0f;
float y = 1.0f - (2.0f * mouse_y) / height;
float z = 1.0f;
vec3 ray_nds = vec3(x, y, z);
第 2 步:4d齐次剪辑坐标

range [-1:1, -1:1, -1:1, -1:1]

我们希望我们的射线的z指向前方 - 这通常是 OpenGL 样式中的负 z 方向。我们可以添加一个 ,这样我们就有一个 4d 向量w, 当然对于QT我们可以用QVector4D替换下面的vec4.

vec4 ray_clip = vec4(ray_nds.xy, -1.0, 1.0);
第 3 步:4d眼(相机)坐标

range [-x:x, -y:y, -z:z, -w:w]

通常,为了从眼睛空间进入剪辑空间,我们将向量乘以 投影矩阵。我们可以通过乘以这个的倒数来倒退 矩阵。

vec4 ray_eye = inverse(projection_matrix) * ray_clip;

现在,我们只需要取消x,y部分的投影,所以让我们手动设置z,w部分的意思是“向前,而不是一个点”。

ray_eye = vec4(ray_eye.xy, -1.0, 0.0);
第 4 步:4d 世界坐标

range [-x:x, -y:y, -z:z, -w:w]

同样,回到转换管道的另一个步骤。记住,我们手动为z分量指定了一个-1,这意味着我们的射线没有归一化。我们应该在使用它之前把它弄清楚。

vec3 ray_wor = (inverse(view_matrix) * ray_eye).xyz;
// don't forget to normalise the vector at some point
ray_wor = normalise(ray_wor);

这将为我们平衡上下、左右和前进组件。所以,假设我们的摄像机直接沿着-Z世界轴看,当鼠标在屏幕中心时,我们应该得到[0,0,-1],而当鼠标在屏幕上移动时,z值就不那么重要了。这将取决于纵横比,以及视图和投影矩阵中定义的视场。我们现在有一条射线,可以和世界空间中的曲面进行比较。

代码展示

// (传入参数为鼠标点击的坐标)计算世界坐标
QVector4D TurboOpenGLWidget::worldPositionFromMousePosition(const QPoint &pos)
{
    float winZ;
    glReadPixels((int)pos.x(), this->height() - (int)pos.y(),
                 1,1,GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
    float x = (2.0 * pos.x()) / this->width() - 1.0f;
    float y = 1.0f - (2.0f * pos.y()) / this->height();
    float z = winZ * 2.0 -1.0f;

    float w = (2.0 * near_ * far_) / (far_ + near_ - z *(far_ - near_));
    QVector4D worldPosition(x, y, z, 1);
    worldPosition *= w;
    worldPosition = view.inverted() * projection.inverted() * worldPosition;
    return worldPosition;
}

模型控制

多模型加载

我们需要用之前模型加载那一块的代码,然后修改成可以加载多个模型的代码。

为此我们需要添加一个类型:

struct ModelInfo
{
    Model *model;
    QVector3D world_pos;
    float pitch;
    float roll;
    float yaw;
    bool is_selected;
    QString name;
}
QMap<QString, ModelInfo> models_;

然后需要将loadModel 修改为多模型的

void TurboOpenGLWidget::loadModel(const QString &file)
{
    makeCurrent();
    static int model_i = 0;
    Model *model = new Model(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Core>(),
                       file.toStdString());
    camera_.setPosition(cameraPositionInit(model->max_y_, model->min_y_));
    models_["aa" + QString::number(model_i++)] = ModelInfo{model, QVector3D(0, 0, 0), 0.0, 0.0, 0.0, false, "aa"};
    doneCurrent();
}

之后需要对绘制的地方进行修改,遍历加载的模型然后绘制对应的模型。

选中模型

选中模型就是需要判断鼠标点击的位置和模型所在位置是否重叠,如果在一定范围内是重叠的则认为我们选中了该模型。

// 判断鼠标是否选中模型
void TurboOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    bool hasSelected=false;
    makeCurrent();
    if(event->buttons() & Qt::LeftButton)
    {
        QVector4D worldPosition;
        worldPosition = worldPositionFromMousePosition(event->pos());
        emit sig_worldPosition(worldPosition);
        for(QMap<QString, ModelInfo>::iterator iter=models_.begin();iter!=models_.end();iter++){
            ModelInfo *modelInfo=&iter.value();
            float r=(modelInfo->model->max_y_-modelInfo->model->min_y_)/2;
            if(modelInfo->world_pos.distanceToPoint(QVector3D(worldPosition))<r
               &&!hasSelected){
                modelInfo->is_selected=true;
                hasSelected=true;
            }
            else
                modelInfo->is_selected=false;
        }
    }
    QWidget::mousePressEvent(event);
}

模型旋转和移动

// 双击时候选中模型
void TurboOpenGLWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    if(model_moving_){
        //再次双击取消移动
        model_moving_=false;
    }else
        foreach(auto modelInfo,models_){
        //双击启动移动
        if(modelInfo.is_selected==true)
            model_moving_=true;
    }

    QWidget::mouseDoubleClickEvent(event);
}
// 如果是移动的状态则移动模型

void TurboOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    makeCurrent();
    static QPoint lastPos(width()/2, height()/2);
    if(model_moving_){
        for(auto iter=models_.begin();iter!=models_.end();iter++){
            ModelInfo *modelInfo=&iter.value();
            if(!modelInfo->is_selected) continue;
            modelInfo->world_pos=
                    QVector3D(worldPositionFromMousePosition(event->pos());
        }
    }
    else if(event->buttons() & Qt::RightButton
       || event->buttons() & Qt::LeftButton
       || event->buttons() & Qt::MiddleButton){

        auto currentPos=event->pos();
        QPoint deltaPos=currentPos-lastPos;
        lastPos=currentPos;
        if(event->buttons() & Qt::RightButton)
            camera_.processMouseMovement(deltaPos.x(),-deltaPos.y());
        else
            for(auto iter=models_.begin();iter!=models_.end();iter++){
                ModelInfo *modelInfo=&iter.value();
                if(!modelInfo->is_selected) continue;
                if(event->buttons() & Qt::MiddleButton){
                    modelInfo->roll+=deltaPos.x();
                }
                else if(event->buttons() & Qt::LeftButton){
                    modelInfo->yaw+=deltaPos.x();
                    modelInfo->pitch+=deltaPos.y();
                }
            }
    }
    doneCurrent();
}

QT+OpenGL鼠标操作和模型控制,QT+OpenGL,qt文章来源地址https://www.toymoban.com/news/detail-518793.html

到了这里,关于QT+OpenGL鼠标操作和模型控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • QT鼠标控制

    每一个setOverrideCursor()都必须最后有一个对应的restoreOverrideCursor()跟着,否则栈将永远不会被清空。 QT中setOverrideCursor 和restoreOverrideCursor函数 clipCursor 函数 (winuser.h)

    2024年02月11日
    浏览(28)
  • QT学习笔记(三)——vs2019+Qt实现打开影像并以鼠标为中心用滚轮控制图片缩放

    之前写了一个博客讲怎么显示一张影像,那个是基于Qpainter的 今天使用QLabel来显示影像,并且用鼠标滚轮控制缩放。 关于图像的打开和显示,主要参考这个博客 关于如何使图片自适应窗口与铺满窗口,可以参考这个博客。 这两个博客出自同一作者,都很详细。 其中按照第二

    2024年02月09日
    浏览(42)
  • Qt OpenGL(四十)——Qt OpenGL 核心模式-雷达扫描效果

    提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 一、场景 上一篇文章介绍了在雷达坐标系中绘制飞行的飞机,其实雷达坐标系应该还有一个效果,就是扫描的效果,类似于下面的图(注:图片来源于百度):  二

    2024年02月13日
    浏览(36)
  • Qt OpenGL(四十二)——Qt OpenGL 核心模式-GLSL(二)

    提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 冯一川注:GLSL其实也是不断迭代的,比如像3.3版本中,基本数据类型浮点型只支持float型,而GLSL4.0版本开始就有double型数据的支持了,所以本系列GLSL部分以GLSL4.5版

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

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

    2023年04月23日
    浏览(41)
  • OpenGL 鼠标拾取模型

    1.简介 在我们的场景中,使用鼠标光标点击或“挑选”一个3d对象是很有用的。一种方法是从鼠标投射3d光线,通过相机,进入场景,然后检查光线是否与任何物体相交。这通常被称为 光线投射 。 我们不是从局部空间中的网格开始,而是从 视口空间中的2d鼠标光标位置开始

    2024年02月12日
    浏览(32)
  • Qt OpenGL相机系统

    效果展示 一直偷懒没有学习OpenGL,乘着这段有点时间重新学习一下OpenGL,做一个简单的小工具,有助于后面理解OSG。我们都知道OpenGL中存在着下面几个坐标空间:模型空间(物体空间)、世界空间、观察空间(或者称作人眼空间)、裁剪空间以及屏幕空间。本质上他们是五个坐

    2024年02月05日
    浏览(46)
  • OpenGL 网格拾取坐标(Qt)

    有时候我们希望通过鼠标来拾取某个网格中的坐标,这就涉及到一个很有趣的场景:光线投射,也就是求取一条射线与网格的交点,这里如果我们采用普通遍历网格中的每个面片的方式,当网格的面片数据量很大时计算效率就很存在问题,因此这里我们使用一种更为理想的方

    2024年01月17日
    浏览(36)
  • 在QT中使用OPENGL的步聚

    OPENGL初始化:   初始化OpenGL函数 : initializeOpenGLFunctions 初始化各种flag :  glEnable 创建各种缓存对象 : VAO,VBO 创建并链接shader :  QOpenGLShaderProgram::addShaderFromSourceCode  QOpenGLShaderProgram::Link 启用shader :  QOpenGLShaderProgram::bind 启用缓存 :     m_vao.bind(); 绑定缓存(加载不变的数据)

    2024年02月09日
    浏览(33)
  • 【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

    通过Qt opengl不是为了3D绘制,而是为了将视频绘制起来 使用opengl 可以极大降低yuv转rgb的转换开销 1、为什么用QT的opengl 简单,界面可以自动叠加 void paintGL(); // 具体的绘制写在该函数里 void initializeGL(); // 材质初始化 void resizeGL(int width, int height); // 当窗口发生变化(缩放) QO

    2023年04月09日
    浏览(50)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包