1.简介

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

我们不是从局部空间中的网格开始,而是从视口空间中的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),以下为公式。

但是实际上,我们要点击到模型,首先是从视口空间坐标开始,也就是从(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(使用世界坐标来计算距离),则选中物体。当然这个算法并不完美。

        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对象,并赋予IDbool 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=newQOpenGLTexture(QImage(":/images/images/container2.png").mirrored());m_PlaneDiffuseTex=newQOpenGLTexture(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 intensitym_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 propertiesm_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博客

OpenGL 鼠标拾取模型相关推荐

  1. OpenGL鼠标拾取

    1.OpenGL自带拾取功能.glSelectBuffer等函数完成. 大每一本OpenGL入门的书都会讲到OpenGL的Select模式,它采用一种方法使得可以获取当前鼠标点选的几何图形.简单的来说 ...

  2. opengl 鼠标拾取

    代码 void GLWidget::HitGet(QPoint pos) { GLuint NameBuffer[27]; GLint viewport[4]; GLint hits; glGetIn ...

  3. opengl作业三维虚拟场景_乱弹OpenGL选择-拾取机制

    无论是游戏还是VR,三维世界总免不了与用户的交互.而这其中常也免不了"用户对场景中物件的选择(也就是,拾取)"这种需求.OpenGL本身就内置有一套拾取机制,这次就乱弹一下吧.(乱 ...

  4. Android OpenGL射线拾取手势旋转(二)

    上回分解-_-!,Android OpenGL射线拾取&手势旋转(一). 3)Renderer:RayPickRenderer.java OpenGL渲染器,比较多的东西都在这里面了. pub ...

  5. DirectX11进阶5_硬件实例化与视锥体裁剪及鼠标拾取交互

    一.硬件实例化(Hardware Instancing) 硬件实例化指的是在场景中绘制同一个物体多次,但是是以不同的位置.旋转.缩放.材质以及纹理来绘制(比如一棵树可能会被多次使用以构建出一片森林). ...

  6. OpenGL3.3鼠标拾取物体

    OpenGL3.3鼠标拾取物体 本文翻译自:http://www.lighthouse3d.com/tutorials/opengl-selection-tutorial/ 在3D场景中拾取或选择特定 ...

  7. Three.js - 鼠标拾取(射线追踪法)(十九)

    射线追踪法 射线追踪法是最常见的方法,因为three.js提供了Raycaster对象来实现它. 原理:从鼠标处发射一条射线,穿透场景的视椎体,通过计算,找出与射线相交的对象. Raycaster 属 ...

  8. OPENGL读取OBJ模型(包围盒、法向等计算)附加源码与资源下载页面

    OPENGL读取OBJ模型 标签(空格分隔): OPENGL/C++ 哈哈,先贴出下载链接好吧.下载页面 首先大家不要害怕,读取obj模型听上去很高端很麻烦,其实当你真正了解obj模型的格式,以及OP ...

  9. 鼠标拾取(光线拾取)

    作者:桑榆 QQ:934440653 有问题,评论留言,或qq联系 案例效果 鼠标拾取 主要代码 1.创建矩形 (1)6-8行限制随机产生的矩形的长.宽.纵深不超过20: (2)14-16行限制随机产 ...

最新文章

  1. S4 HANA物料凭证表MATDOC
  2. 解决linux mysql命令 bash: mysql: command not found 的方法
  3. CSS 从入门到放弃系列:CSS的引入方式
  4. 嵌入式系统 Boot Loader 技术内幕-目前看到介绍Boot loader最通俗明了的文章
  5. Python入门100题 | 第018题
  6. java获取本地外网ip地址
  7. halcon trainf_ocr_class_svm 训练OCR分类器
  8. Simulink之三相桥式半控整流电路
  9. 关于SRAM,DRAM,SDRAM,以及NORFLASH,NANDFLASH
  10. Maven学习总结(9)——使用Nexus搭建Maven私服
  11. python是动态_Python中的对象和动态性 [菜鸟的理解,高手莫入]
  12. JSON.stringify的认知历程
  13. 离散数学及其应用 第三章:命题逻辑
  14. SIPP介绍以及使用介绍
  15. 【第五课】ArcCatalog和ArcToolbox初识
  16. 【摘录】B2C大点名:国内B2C网站收集
  17. html公用页脚使用代码,页脚在HTML
  18. android 拔插键盘自动切换输入法
  19. 不支持IE8及以下版本
  20. MFC中进度条的学习

热门文章

  1. 【RTCP】malformed packet
  2. 前端面试题全集(html+CSS)
  3. 华师2020计算机专业宿舍,2020华中师范大学宿舍条件好不好-有空调否?(宿舍图片)...
  4. 城乡投票源码php_PHP发表心情投票功能示例(附源码)
  5. Python+Vue计算机毕业设计基于的营养配餐评价系统d6255(源码+程序+LW+部署)
  6. 如何用css实现波纹动画效果
  7. html如何实现立体效果,纯css实现立体摆放图片效果的示例代码
  8. SpringBoot 指标监控
  9. 【电力电子技术DC-AC】三相SPWM逆变器Simulink仿真
  10. Tensorflow2.0 复现 NNLM