最近利用opengl+QT实现了一个3D点云,并且点击点云的时候可以画出一个以选中点为原点的三维坐标系,可以实现移动和旋转,大概效果如下:

(图中的小黄圆是截屏软件导致的,并非实际效果。)

在这里分享几个关键函数。

将屏幕坐标转换为opengl的世界坐标:

QVector3D GlDisplayWidget::mousePosToWorldPos(QPoint _pos)
{double  modelview[ 16 ], projection[ 16 ];int  viewport[ 4 ];float  x = _pos.x();float  y = _pos.y();GLfloat  z =  0 ;double  objx, objy, objz;/*Read the projection, modelview and viewport matrices using the glGet functions.*/glGetIntegerv( GL_VIEWPORT, viewport );glGetDoublev( GL_MODELVIEW_MATRIX, modelview );glGetDoublev( GL_PROJECTION_MATRIX, projection );//Read the window z value from the z-bufferglReadBuffer(GL_FRONT);glReadPixels( x, viewport[ 3 ]-y,  1 ,  1 , GL_DEPTH_COMPONENT, GL_FLOAT, &z );//Use the gluUnProject to get the world co-ordinates of//the point the user clicked and save in objx, objy, objz.gluUnProject( x, viewport[ 3 ]-y, z, modelview, projection, viewport, &objx, &objy, &objz );return QVector3D(objx,objy,objz);
}

我们知道,在opengl中,从世界坐标到屏幕坐标的顺序是:

所以我们只要使用glReadPixels获取该屏幕坐标的深度值,然后读取最后进行的变换矩阵,就可以通过gluUnProject获取屏幕坐标对应的世界坐标,就可以实现点云的选中功能了。由于QT屏幕坐标系与opengl上下相反,所以调用的时候需要将y改成viewport[3]-y。

将世界坐标转换为屏幕坐标:

QPointF GlDisplayWidget::worldPosToScreenPos(QVector3D pos)
{double  modelview[ 16 ], projection[ 16 ];int  viewport[ 4 ];//double  objx, objy, objz;double x,y,z;/*Read the projection, modelview and viewport matrices using the glGet functions.*/glGetIntegerv( GL_VIEWPORT, viewport );glGetDoublev( GL_MODELVIEW_MATRIX, modelview );glGetDoublev( GL_PROJECTION_MATRIX, projection );gluProject(pos.x(),pos.y(),pos.z(),modelview, projection, viewport,&x,&y,&z);return QPointF(x,viewport[ 3 ]-y);
}

类似的,我们可以使用gluProject将世界坐标转换为屏幕坐标。

接下来是坐标系的绘制,由于我使用的是,z-y-x欧拉角变换,所以旋转都是先绕z轴旋转,再绕y轴,最后绕x轴。

坐标轴渲染的接口:

void GlDisplayWidget::draw3DCoord(customPoint point)
{double x,y,z,rx,ry,rz;x = point.x;y = point.y;z = point.z;rx = point.rotateX;ry = point.rotateY;rz = point.rotateZ;const double len = 2.0;const double k = 0.75;const double vlen = len*(1-k);const double step =0.707/25.0;GLUquadric* obj = gluNewQuadric();glColor3f(0.745,0.745,0.745);glPushMatrix();glTranslated(x,y,z);gluSphere(obj,0.1,20,20);glPopMatrix();glPushMatrix();//x轴glTranslated(x,y,z);glRotated(rz,0.0,0.0,1.0);glRotated(ry,0.0,1.0,0.0);glRotated(rx,1.0,0.0,0.0);if(curClickType == rxCenter){glRotated(rotateAngel,1.0,0.0,0.0);}else if(curClickType == ryCenter){glRotated(rotateAngel,0.0,1.0,0.0);}else if(curClickType == rzCenter){glRotated(rotateAngel,0.0,0.0,1.0);}{//圆柱if(curClickType == txCenter){glColor3f(1.0,0.0,1.0);}else{glColor3f(1.0,0.0,0.0);}glPushMatrix();glRotatef(90, 0.0, 1.0, 0.0);//glTranslated(x,y,z);gluCylinder(obj,0.02,0.02,len*k,30,30);glTranslated(0.0,0.0,len-vlen);gluCylinder(obj,0.1,0.00,0.5,30,30);//z旋转球glRotatef(-90, 0.0, 1.0, 0.0);glTranslated(vlen-len+0.5,0.5,0.0);if(curClickType == rzCenter){glColor3f(1.0,0.0,1.0);}else{glColor3f(0.0,0.0,1.0);}gluSphere(obj,0.12,20,20);glPopMatrix();//绘制弧线x-yglLineWidth(1);glBegin(GL_LINE_STRIP);for(double x1 = 0.0;x1<=0.707;x1 += step){double y1 = sqrt(0.50 - x1*x1);glVertex3d(x1,y1,0.0);}glEnd();}//y轴{//圆柱if(curClickType == tyCenter){glColor3f(1.0,0.0,1.0);}else{glColor3f(0.0,1.0,0.0);}glPushMatrix();glRotatef(90, -1.0, 0.0, 0.0);//glTranslated(x,y,z);gluCylinder(obj,0.02,0.02,1.8,30,30);glTranslated(0.0,0.0,len-vlen);gluCylinder(obj,0.1,0.00,0.5,30,30);//x旋转球glRotatef(-90, -1.0, 0.0, 0.0);glTranslated(0.0,vlen-len+0.5,0.5);if(curClickType == rxCenter){glColor3f(1.0,0.0,1.0);}else{glColor3f(1.0,0.0,0.0);}gluSphere(obj,0.12,20,20);glPopMatrix();//绘制弧线y-zglLineWidth(1);glBegin(GL_LINE_STRIP);for(double y1 = 0.0;y1<=0.707;y1 += step){double z1 = sqrt(0.50 - y1*y1);glVertex3d(0.0,y1,z1);}glEnd();}//z轴{//圆柱if(curClickType == tzCenter){glColor3f(1.0,0.0,1.0);}else{glColor3f(0.0,0.0,1.0);}glPushMatrix();//glTranslated(x,y,z);gluCylinder(obj,0.02,0.02,1.8,30,30);glTranslated(0.0,0.0,len-vlen);gluCylinder(obj,0.1,0.00,0.5,30,30);//y旋转球glTranslated(0.5,0.0,vlen-len+0.5);if(curClickType == ryCenter){glColor3f(1.0,0.0,1.0);}else{glColor3f(0.0,1.0,0.0);}gluSphere(obj,0.12,20,20);glPopMatrix();//绘制弧线x-zglLineWidth(1);glBegin(GL_LINE_STRIP);for(double x1 = 0.0;x1<=0.707;x1 += step){double z1 = sqrt(0.50 - x1*x1);glVertex3d(x1,0.0,z1);}glEnd();}glPopMatrix();
}

在函数中,我们使用了opengl绘制图元的方式画作标轴,使用gluSphere绘制球体,gluCylinder绘制圆柱来当坐标轴,gluCylinder来绘制圆锥。绘制圆弧的时候,计算一部分圆弧上的点,再将这些点连起来,就是弧线,效果如下图:

坐标轴的移动比较简单,但是旋转因为改变旋转顺序会导致结果不同,在这里,我才用了欧拉角旋转公式和逆转公式实现了旋转。

点绕指定轴旋转a的函数:

QVector3D GlDisplayWidget::Rotate(QVector3D p1, QVector3D p2, double angle)
{double old_x,old_y,old_z;double vx,vy,vz;old_x = p1.x();old_y = p1.y();old_z = p1.z();vx = p2.x();vy = p2.y();vz = p2.z();double c = cos(angle/180.0*3.14);double s = sin(angle/180.0*3.14);double new_x = (vx*vx*(1 - c) + c) * old_x + (vx*vy*(1 - c) - vz*s) * old_y + (vx*vz*(1 - c) + vy*s) * old_z;double new_y = (vy*vx*(1 - c) + vz*s) * old_x + (vy*vy*(1 - c) + c) * old_y + (vy*vz*(1 - c) - vx*s) * old_z;double new_z = (vx*vz*(1 - c) - vy*s) * old_x + (vy*vz*(1 - c) + vx*s) * old_y + (vz*vz*(1 - c) + c) * old_z;return QVector3D(new_x,new_y,new_z);
}

已知坐标轴从状态a到状态b,获取从a到b旋转角度的函数:

QVector3D GlDisplayWidget::fromRotate(QVector3D x,QVector3D y,QVector3D z)
{double r11,r12,r13,r21,r22,r23,r31,r32,r33;r11 = x.x();r12 = y.x();r13 = z.x();r21 = x.y();r22 = y.y();r23 = z.y();r31 = x.z();r32 = y.z();r33 = z.z();QVector3D result;double cb = sqrt(r11*r11+r21*r21);if(cb != 0){result.setY(atan2(0-r31,sqrt(r11*r11+r21*r21))/3.14*180.0);result.setZ(atan2(r21,r11)/3.14*180.0);result.setX(atan2(r32,r33)/3.14*180.0);return result;}else{result.setY(90.0);result.setZ(0);result.setX(atan2(r21,r22)/3.14*180.0);return result;}
}

这个接口得出来的角度一定是从z轴旋转c,y轴旋转b,x轴旋转a,得到了角度a,b,c。具体原理大家可以去了解欧拉角和RPY角。

如果想给窗口加一个背景图,可以先切换正交视角,关闭深度测试,加载图片,然后再切换投影视角,开启深度测试,再进行渲染。

QT+opengl实现3D点云和三维坐标系相关推荐

  1. 3D转换之三维坐标系,透视旋转等基础知识

    一.三维坐标系 三维坐标系就是指立体空间,立体空间是由3个轴共同组成的. 1.x轴:水平向右    注意:x右边是正值,左边是负值 2.y轴:垂直向下    注意:y下面是正值,上面是负值 3.z轴: ...

  2. 【Qt OpenGL】Qt Creator中的3D绘图及动画教程

    Qt Creator中的3D绘图及动画教程(参照NeHe) 刚刚学习了Qt Creator,发现Qt提供了QtOpenGL模块,对OpenGL做了不错的封装,这使得我们可以很轻松地在Qt程序中使用Op ...

  3. Qt+OpenGL实现三维地形显示

    因为项目需要,要用Qt+OpenGL显示三维地形,业务代码涉及保密,但是这种纯技术上的东西还是可以分享的. 话不多说,先看效果 这里我介绍一个简单的使用QT中的OpenGL实现三维地形显示的demo, ...

  4. QT + opengl 三维视图变换

    QT + Opengl 三维视图变换 原本是导师的项目,要求像paraview一样实现三维网格的可视化以及三键操作,从qt 和opengl零基础开始动手鼓捣了大半个月的才完成 QT +Opengl环境 ...

  5. 3D图形学(一):三维几何学基础(1):三维坐标系

    原文链接:http://my.oschina.net/ioslighter/blog/358099 左手坐标系和右手坐标系 例: OpenGL用的是右手坐标系,原点在左下角,向右为x轴正方向,向上为y ...

  6. Qt OpenGL(三十六)——Qt OpenGL 核心模式-绘制雷达坐标系

    提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(三十六)--Qt OpenGL 核心模式-绘制雷达坐标系 一.场景 ...

  7. OpenGL(十八)——Qt OpenGL绘制一个3D世界

    OpenGL(十八)--Qt OpenGL绘制一个3D世界 一.说明 本篇介绍构建一个3D的世界. 二.简介 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游. ...

  8. Qt之实现3D纹理渲染自由旋转空间立方体

    昨天七夕,关于七夕美好的爱情传说源自于浩瀚银河星空,又碰巧最近在学习QtOpenGL实现三维纹理防体重建,突发奇想用Qt实现一个立方体星空模型,并且能随着鼠标操作实现空间自由旋转 核心思想是用到Qt ...

  9. OpenGL(十四)——Qt OpenGL纹理

    OpenGL(十四)--Qt OpenGL纹理 一.纹理 终于写到纹理的部分了: 纹理(Texture)的本质是一个2D图片(1D和3D),或者叫图形数据.只是在OpenGL中专业术语中称其为纹理. ...

最新文章

  1. 实现通用人工智能还要多久?Hinton与AlphaGo之父这样回答
  2. Java 类的生命周期详解
  3. QT消息/事件循环机制与多线程的关系
  4. no typehandler found for property XXXX 解决
  5. win7磁盘清理_为什么要清理磁盘碎片,win7电脑怎么清理磁盘碎片
  6. FlexSPI对写时序支持
  7. Python基础语句
  8. 实现复制文本到剪贴板功能
  9. FastFDS分布式文件系统
  10. ANSYS模态分析详细步骤记录
  11. matlab中uigetfile函数的使用
  12. 逐点比较法直线插补MATLAB(四个象限合并)
  13. Exchange 2010环境部署2
  14. 博客披着个人外衣的集体游戏
  15. 人力资源领域RPA的应用场景
  16. 【Hardware】Mac外接DELL显示屏时报错“No DP Signal”无法连接
  17. python画风景图_风景侠
  18. 安装CocoaPods出现错误解决方法
  19. Android辅助H5做一个Web版的相册功能
  20. 程序员春招是什么时候?什么是金三银四?

热门文章

  1. SVN移库项目迁移、更换根目录、移库完美教程!
  2. 收集癖:Unity开源插件
  3. opencv-python读取透明图片(既带alpha通道)
  4. 基于5G承载数通的PTN网络
  5. PTN设备和SDH设备有什么区别?可以互通吗?
  6. 三甲医院医生曝20年工资涨20倍 隐性收入远超工资
  7. 聚划算安卓客户端1期教训总结
  8. java毕业设计大型商场应急预案管理系统mybatis+源码+调试部署+系统+数据库+lw
  9. OmniGraffle 一款曾获得苹果设计大奖的绘图软件
  10. 拔出来, 别插着睡。