OpenGL 鼠标拾取模型

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

1.简介

在我们的场景中,使用鼠标光标点击或“挑选”一个3d对象是很有用的。一种方法是从鼠标投射3d光线,通过相机,进入场景,然后检查光线是否与任何物体相交。这通常被称为光线投射

OpenGL 鼠标拾取模型,OpenGL,opengl,qt,模型加载,鼠标选中

我们不是从局部空间中的网格开始,而是从视口空间中的2d鼠标光标位置开始。我们用逆矩阵来逆向进行变换,得到世界空间中的一条射线。

  • 首先在局部空间中有一个局部坐标(0.5,1,0)
  • 然后乘以model矩阵变换到世界空间坐标(10.5,1,-20,1),这里增加了齐次坐标w=1
  • 然后世界空间坐标乘以view矩阵转换到我们摄像机看到的坐标(5,-1,-9.5,1)
  • 然后观察坐标乘以projection矩阵转换到裁剪空间(9.05,-2.41,7.67,9.5),这里齐次坐标变为-z
  • 然后标准化设备坐标到(-1,1)的空间中,使用x/w,y/w,z/w得到(0.95,-0.25,0.81)
  • 最后转换到像素坐标,转换到(1024,768)这个坐标下,得到(998,288),以下为公式。

OpenGL 鼠标拾取模型,OpenGL,opengl,qt,模型加载,鼠标选中

 

但是实际上,我们要点击到模型,首先是从视口空间坐标开始,也就是从(998,288)开始,进行反推。

2.计算世界空间坐标

float _near=0.1f,_far=100.0f;
QVector4D AXBOpemglWidget::worldPosFromViewPort(int posX, int posY)
{
    float winZ;
    glReadPixels(
                posX,
                this->height()-posY
                ,1,1
                ,GL_DEPTH_COMPONENT,GL_FLOAT
                ,&winZ);
    qDebug()<<"z = "<<winZ; //获取深度值
    float x=(2.0f*posX)/this->width()-1.0f; //转化为标准设备坐标(-1,1)
    float y=1.0f-(2.0f*posY)/this->height();//转化为标准设备坐标(-1,1)
    float z=winZ*2.0-1.0f;//转化为标准设备坐标(-1,1)

    float w = (2.0 * _near * _far) / (_far + _near - z * (_far - _near));//计算齐次坐标
    //float w= _near*_far/(_near*winZ-_far*winZ+_far);
    QVector4D wolrdPostion(x,y,z,1);
    wolrdPostion=w*wolrdPostion;//裁剪空间的坐标
    return view.inverted()*projection.inverted()*wolrdPostion; //获得世界空间的坐标
}
  • 首先传入像素坐标x,y
  •  glReadPixels获取到深度值
  • 然后标准化坐标(x,y,z)
  • 计算齐次坐标
  • 使用w*x,w*y,w*z得到裁剪空间坐标
  • 最后乘以projection逆矩阵 view的逆矩阵得到世界空间坐标

3.如何判断点中了模型

这里给出了一个简单的算法,把模型比作一个球体,计算点击位置P点到模型中心O点的距离,如果小于半径r(使用世界坐标来计算距离),则选中物体。当然这个算法并不完美。

OpenGL 鼠标拾取模型,OpenGL,opengl,qt,模型加载,鼠标选中

 

        for(QMap<QString, ModelInfo>::iterator iter=m_Models.begin();iter!=m_Models.end();iter++){
            ModelInfo *modelInfo=&iter.value();
            float r=(modelInfo->model->m_maxY-modelInfo->model->m_minY)/2;
            if(modelInfo->worldPos.distanceToPoint(QVector3D(wolrdPostion))<r
                    &&!hasSelected){
                modelInfo->isSelected=true;
                hasSelected=true;
            }
        }

4.源码

#include "axbopemglwidget.h"
#include "vertices.h"
const unsigned int timeOutmSec=50;

QVector3D viewInitPos(0.0f,5.0f,20.0f);
float _near=0.1f,_far=100.0f;
QMatrix4x4 model;
QMatrix4x4 view;
QMatrix4x4 projection;
AXBOpemglWidget::AXBOpemglWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    connect(&m_timer,SIGNAL(timeout()),this,SLOT(on_timeout()));
    m_timer.start(timeOutmSec);
    m_time.start();
    m_camera.Position=viewInitPos;
    setFocusPolicy(Qt::StrongFocus);
    //setMouseTracking(true);
}

AXBOpemglWidget::~AXBOpemglWidget()
{
    for(auto iter=m_Models.begin();iter!=m_Models.end();iter++){
        ModelInfo *modelInfo=&iter.value();
        delete modelInfo->model;
    }
}

void AXBOpemglWidget::loadModel(string path)
{
    static int i=0;
    makeCurrent();
    Model * _model=new Model(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>()
                             ,path.c_str());
    qDebug()<<"miny = "<<_model->m_minY;
    //m_camera.Position=cameraPosInit(_model->m_maxY,_model->m_minY);
    m_Models["张三"+QString::number(i++)]=
            ModelInfo{_model,QVector3D(0,0-_model->m_minY,0),0.0,0.0,0.0,false,"张三"};
    doneCurrent();
}

void AXBOpemglWidget::initializeGL()
{
    initializeOpenGLFunctions();
    //创建VBO和VAO对象,并赋予ID
    bool success;
    m_ShaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shaders/shapes.vert");
    m_ShaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shaders/shapes.frag");
    success=m_ShaderProgram.link();
    if(!success) qDebug()<<"ERR:"<<m_ShaderProgram.log();
    m_BoxDiffuseTex=new
            QOpenGLTexture(QImage(":/images/images/container2.png").mirrored());
    m_PlaneDiffuseTex=new
            QOpenGLTexture(QImage(":/images/images/wall.jpg").mirrored());

    m_PlaneMesh=processMesh(planeVertices,6,m_PlaneDiffuseTex->textureId());
}

void AXBOpemglWidget::resizeGL(int w, int h)
{
    Q_UNUSED(w);
    Q_UNUSED(h);
}

void AXBOpemglWidget::paintGL()
{
    model.setToIdentity();
    view.setToIdentity();
    projection.setToIdentity();
    // float time=m_time.elapsed()/50.0;
    projection.perspective(m_camera.Zoom,(float)width()/height(),_near,_far);
    view=m_camera.GetViewMatrix();
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    m_ShaderProgram.bind();
    m_ShaderProgram.setUniformValue("projection", projection);
    m_ShaderProgram.setUniformValue("view", view);
    //model.rotate(time, 1.0f, 1.0f, 0.5f);

    m_ShaderProgram.setUniformValue("viewPos",m_camera.Position);

    // light properties, note that all light colors are set at full intensity
    m_ShaderProgram.setUniformValue("light.ambient", 0.7f, 0.7f, 0.7f);
    m_ShaderProgram.setUniformValue("light.diffuse", 0.9f, 0.9f, 0.9f);
    m_ShaderProgram.setUniformValue("light.specular", 1.0f, 1.0f, 1.0f);
    // material properties
    m_ShaderProgram.setUniformValue("material.shininess", 32.0f);
    m_ShaderProgram.setUniformValue("light.direction", -0.2f, -1.0f, -0.3f);
    m_ShaderProgram.setUniformValue("model", model);

    m_PlaneMesh->Draw(m_ShaderProgram);

    foreach(auto modelInfo,m_Models){
        model.setToIdentity();
        model.translate(modelInfo.worldPos);

        model.rotate(modelInfo.pitch,QVector3D(1.0,0.0,0.0));
        model.rotate(modelInfo.yaw,QVector3D(0.0,1.0,0.0));
        model.rotate(modelInfo.roll,QVector3D(0.0,0.0,1.0));
        m_ShaderProgram.setUniformValue("model", model);
        modelInfo.model->Draw(m_ShaderProgram);
    }

}

void AXBOpemglWidget::wheelEvent(QWheelEvent *event)
{
    m_camera.ProcessMouseScroll(event->angleDelta().y()/120);
}

void AXBOpemglWidget::keyPressEvent(QKeyEvent *event)
{
    float deltaTime=timeOutmSec/1000.0f;

    switch (event->key()) {
    case Qt::Key_W: m_camera.ProcessKeyboard(FORWARD,deltaTime);break;
    case Qt::Key_S: m_camera.ProcessKeyboard(BACKWARD,deltaTime);break;
    case Qt::Key_D: m_camera.ProcessKeyboard(RIGHT,deltaTime);break;
    case Qt::Key_A: m_camera.ProcessKeyboard(LEFT,deltaTime);break;
    case Qt::Key_Q: m_camera.ProcessKeyboard(DOWN,deltaTime);break;
    case Qt::Key_E: m_camera.ProcessKeyboard(UP,deltaTime);break;
    case Qt::Key_Space: m_camera.Position=viewInitPos;break;

    default:break;
    }
}

void AXBOpemglWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() & Qt::RightButton){
        static QPoint lastPos(width()/2,height()/2);
        auto currentPos=event->pos();
        QPoint deltaPos=currentPos-lastPos;
        lastPos=currentPos;

        m_camera.ProcessMouseMovement(deltaPos.x(),-deltaPos.y());
    }
}

void AXBOpemglWidget::mousePressEvent(QMouseEvent *event)
{
    makeCurrent();
    if(event->buttons()&Qt::LeftButton){

        QVector4D wolrdPostion=worldPosFromViewPort(event->pos().x(),
                                                    event->pos().y());

        mousePickingPos(QVector3D(wolrdPostion));

        for(QMap<QString, ModelInfo>::iterator iter=m_Models.begin();iter!=m_Models.end();iter++){
            ModelInfo *modelInfo=&iter.value();
            float r=(modelInfo->model->m_maxY-modelInfo->model->m_minY)/2;
            if(modelInfo->worldPos.distanceToPoint(QVector3D(wolrdPostion))<r){
                qDebug()<<"selected";
            }
            }
    }

    doneCurrent();
}

void AXBOpemglWidget::on_timeout()
{
    update();
}

QVector3D AXBOpemglWidget::cameraPosInit(float maxY, float minY)
{
    QVector3D temp={0,0,0};
    float height=maxY-minY;
    temp.setZ(1.5*height);
    if(minY>=0)
        temp.setY(height/2.0);
    viewInitPos=temp;
    return temp;
}

Mesh* AXBOpemglWidget::processMesh(float *vertices, int size, unsigned int textureId)
{
    vector<Vertex> _vertices;
    vector<unsigned int> _indices;
    vector<Texture> _textures;
    //memcpy(&_vertices[0],vertices,8*size*sizeof(float));
    for(int i=0;i<size;i++){
        Vertex vert;
        vert.Position[0]=vertices[i*5+0];
        vert.Position[1]=vertices[i*5+1];
        vert.Position[2]=vertices[i*5+2];
        vert.TexCoords[0]=vertices[i*5+3];
        vert.TexCoords[1]=vertices[i*5+4];
        _vertices.push_back(vert);
        _indices.push_back(i);
    }
    Texture tex; tex.id=textureId;
    tex.type="texture_diffuse";
    _textures.push_back(tex);
    return new Mesh(
                QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>()
                ,_vertices,_indices,_textures);
}

QVector4D AXBOpemglWidget::worldPosFromViewPort(int posX, int posY)
{
    float winZ;
    glReadPixels(
                posX,
                this->height()-posY
                ,1,1
                ,GL_DEPTH_COMPONENT,GL_FLOAT
                ,&winZ);
    float x=(2.0f*posX)/this->width()-1.0f;
    float y=1.0f-(2.0f*posY)/this->height();
    float z=winZ*2.0-1.0f;

    float w = (2.0 * _near * _far) / (_far + _near - z * (_far - _near));
    //float w= _near*_far/(_near*winZ-_far*winZ+_far);
    QVector4D wolrdPostion(x,y,z,1);
    wolrdPostion=w*wolrdPostion;
    return view.inverted()*projection.inverted()*wolrdPostion;
}

5.完整工程https://download.csdn.net/download/wzz953200463/87941643https://download.csdn.net/download/wzz953200463/87941643

6.相关参考 

OpenGL 齐次坐标_Mr.codeee的博客-CSDN博客

OpenGL 坐标系统_Mr.codeee的博客-CSDN博客

OpenGL模型加载_Mr.codeee的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-525998.html

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

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

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

相关文章

  • OpenGL加载模型 之网格

    我们的工作就是去解析这些导出的模型文件,并将其中的模型数据存储为OpenGL能够使用的数据。一个常见的问题是,导出的模型文件通常有几十种格式,不同的工具会根据不同的文件协议把模型数据导出到不同格式的模型文件中。 有的模型文件格式只包含模型的静态形状数据

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

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

    2024年02月06日
    浏览(37)
  • OpenGL ES相关库加载3D 车辆模型

    需求类似奇瑞的这个效果,就是能全方位旋转拖拽看车,以及点击开关车门车窗后备箱等 瑞虎9全景看车 (chery.cn) 最开始收到这个需求的时候还有点无所适从,因为以前没有做过类似的效果,后面一经搜索后发现实现的方式五花八门,但始终绕不过opengl,于是通过opengl 逐步展

    2024年02月06日
    浏览(36)
  • Qt OpenGL 3D模型

    这次教程中,我们将之前几课的基础上,教大家如何创建立体的3D模型。我们将开始生成真正的3D对象,而不是像之前那几课那样3D世界中的2D对象。我们会把之前的三角形变为立体的金字塔模型,把四边形变为立方体。 我们给三角形增加左侧面、右侧面、后侧面来生成一个金

    2024年02月11日
    浏览(49)
  • OpenGL Assimp加载各类型模型(.obj、.fbx、.glb、.3ds)

    1.简介 本博客以.glb格式为例,加载glb格式的3d模型,网上找了一圈,基本上都是根据OpenGL官方示例,加载.obj格式的3d模型。 下面以.obj和.glb格式的3D模型简单介绍一下。 常见的.obj格式的3D模型如下所示:纹理都已经被剥离出来了。所以在使用Assimp库加载的时候,加载了指定的

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

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

    2024年02月13日
    浏览(36)
  • 9. QML_OpenGL--2. 在QQuick中搭建加载OpenGL框架

    1. 说明: OPenGL一般在 QtWidget 中使用,但目前使用 QML 做界面开发是一种趋势,同时在QML中使用OPenGL进行渲染也是十分必要,文章简单介绍如何在QML中使用 OPenGL,搭建了一种基本的框架。整体思路和在 QtWidget 中类似,只不过在 QML 中需要定义一个中间辅助类,这个类是用来注

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

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

    2024年02月08日
    浏览(48)
  • 使用opengl绘制茶壶并实现鼠标拖动

    难点如下:         坐标轴绘制             选定一个原点,将坐标轴正方向和反方向的俩个点进行连线,代码及效果如上图所示(本次程序中由于渲染原因,坐标轴颜色统一为棕色)         如何实现鼠标响应         OPENGL中封存有对鼠标进行相应的函数,

    2024年01月17日
    浏览(39)
  • Qt Quick 3D学习:鼠标拾取物体

    (注意,开源版的 Qt Quick 3D 是狗都不用的 GPL 协议) Qt Creator 中有一个 picking 的示例,用于演示 View3D 中物体的拾取: 在示例基础上,我又加了一个简单的拖动效果,如图所示:   在使用 OpenGL 实现拾取的时候,我们可以用射线法。Qt Quick 3D 中封装了拾取操作,通过 View3D 的

    2024年02月10日
    浏览(40)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包