QT框架下的OpenGL使用---实战篇---点云显示和交互

阅读本篇文章需要提前掌握OpenGL顶点和着色器摄像机的相关知识。

前面复现篇的两篇文章中介绍了Qt+OpenGL框架下顶点和着色器及摄像机的知识,接下来我们用这两个知识来实现3D领域非常常见的任务—点云显示和交互。

点云的显示

3D领域常见的一个需求是将点云显示出来给用户,这个功能乍一看好像还比较复杂,实则不然,只要我们学会OpenGL的顶点和着色器的知识就能轻松搞定。

原理很简单,只要直接将点云当成是OpenGL的"点"图元进行处理就可以了。在顶点和着色器的复现文章中,我们绘制的图元是三角形,代码如下:

f->glDrawArrays(GL_TRIANGLES, 0, 3);

只要我们提前将点云全部当成顶点并放到VBO中,然后将图元类型改成点,问题就完美解决了:

f->glDrawArrays(GL_POINTS, 0, 3);//最后一个参数可以改为点云的个数

观察尺度的变换

在很多情况下我们需要放大和缩小对象,以更好的对3D点云进行观测,这个功能可以通过修改摄像机的视角大小来实现。详细的介绍可以看这里摄像机,这里给出实现代码片段:

将摄像头视角大小以变量进行表示:

mvp.perspective(field, aspectRatio, 0.1f, 100.0f);//field是视角大小的变量

在鼠标滚轮响应函数中,修改视角的大小

void MyOpenGLWidget::wheelEvent(QWheelEvent *event)// 滚轮事件
{
    if(event->delta() > 0)// 当滚轮远离使用者时
    {
        if(field>1)
        {
            field--;
        }
    }
    else// 当滚轮向使用者方向旋转时
    {
        if(field<170)
        {
            field++;
        }
    }
    this->update();
}

点云的平移

点云的平移其实就相当于移动世界的中心观察点,可以通过mvp对象的translate函数来实现

QVector3D offset;//偏移量
mvp.translate(offset);//图像平移

点云的旋转

点云的旋转可以使用摄像机文章中介绍的rotate函数来实现,这里以自动驾驶的点云处理为例做简单的介绍。

在自动驾驶中,使用激光雷达获得的点云投影到世界坐标中,一般是以坐标系的xoy为地平面的,而z轴为高度。因此,出于直观的原因,大部分的软件会提供用户两种点云的旋转观测方式:一是将点云绕着z轴进行旋转(保持地面不变,车子周围的物体绕车中轴旋转),二是将点云绕着x轴进行旋转(相当于车子上下俯仰的视角)。

以ROS中的Rviz为例,当鼠标左键按住然后向左移动时,程序根据移动的幅度将点云绕着z轴顺时针旋转,向右则绕着z轴逆时针旋转。当左键按住然后向上移动时,程序会根据移动的幅度将点云绕着x轴顺时针旋转,向下则为绕着x轴逆时针旋转。

因此我们只需要在鼠标按下时记住鼠标指针的位置以及当前点云的旋转状态:

void MyOpenGLWidget::mousePressEvent(QMouseEvent *event) // 鼠标按下事件
{
    if(event->button() == Qt::LeftButton)
    {
        offset = event->globalPos();// 获取指针位置
        oldrotatex=rotatex;
        oldrotatez=rotatez;
    }
}

然后在鼠标移动响应函数中判断左键是否被按下,如果是计算偏差值并设置点云绕轴旋转的对应参数即可:

void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event) // 鼠标移动事件
{
    if(event->buttons() & Qt::LeftButton)//按下左键并且移动
    {
        QPoint temp;
        temp = event->globalPos() - offset;//获取移动值
        rotatex=oldrotatex+temp.y()/20.0;
        rotatez=oldrotatez+temp.x()/20.0;
    }
    this->update();
}

旋转函数修改如下:

mvp.rotate(10.0f * rotatex, QVector3D(1.0f, 0.0f, 0.0f));//绕x轴旋转
mvp.rotate(10.0f * rotatez, QVector3D(0.0f, 0.0f, 1.0f));//绕z轴旋转

点云平移后旋转

在rviz中我们通过鼠标中键平移点云,之后旋转点云对象会发现旋转中心还在原来的位置,因此操作起来非常别扭。要实现那样的效果只要结合我们上面写的平移和旋转就可以了:

QMatrix4x4 mvp;
mvp.translate(worldcenter);//通过鼠标中键等操作点云的平移,这里不再赘述
mvp.rotate(10.0f * rotatex, QVector3D(1.0f, 0.0f, 0.0f)); //绕x轴旋转
mvp.rotate(10.0f * rotatez, QVector3D(0.0f, 0.0f, 1.0f)); //绕z轴旋转

但这不是我们理想中的操作,一般习惯上我们是希望移动后以新的中心进行旋转的,因此我们需要把代码修改成下面这样:

QMatrix4x4 mvp;
mvp.rotate(10.0f * rotatex, QVector3D(1.0f, 0.0f, 0.0f)); //绕x轴旋转
mvp.rotate(10.0f * rotatez, QVector3D(0.0f, 0.0f, 1.0f)); //绕z轴旋转
mvp.translate(worldcenter);//通过鼠标中键等操作点云的平移,这里不再赘述

事实上就是换了个顺序,咋一看非常奇怪,似乎之前的那个才是对的,因为平移写在了旋转之前。这样写的原因是QMatrix4x4 对象操作的内部实现是从下往上的,因此才有这个问题。

这个坑让本人一度放弃这个实现的方式,而用曲线救国的方法来实现点云的平移和旋转。在最近一次修改的时候突然想起来之前一篇已经忘了名字的博客上有提到这个问题,一式才恍然大悟。。。

点云的大小

initializeGL()中加入:

f->glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);//开启点大小调节功能

然后在顶点着色器中加入点大小设置代码即可:

gl_PointSize = 10.0f;//设置点的大小为10

点云的更新

点云的更新,本人采取的办法是不断的更新VBO的顶点数据,代码如下:

m_painting->m_vbo->bind();
for(int i=0;i<ptcloud->points.size();i++)
{
    vertices[i * 3 + 0] = ptcloud->points[i].x;
    vertices[i * 3 + 1] = ptcloud->points[i].y;
    vertices[i * 3 + 2] = ptcloud->points[i].z;
}
m_painting->m_vbo->allocate(vertices, ptcloud->points.size() * 3 * sizeof(GLfloat));
m_painting->f->glEnableVertexAttribArray(0);
m_painting->f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,3*sizeof(GLfloat), 0);
m_painting->m_vbo->release();
m_painting->update();

然而个人感觉这么写应该不是最好的,每帧数据更新都会去内存中拿数据之后更新到显存,这里应该有将内存和显存进行更为高效绑定的方法,后面再研究。

image-20200814013626847

  • 6
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
QOpenGL是一个用于在Qt应用程序中使用OpenGL功能的模块。QObjectPicker是QOpenGL提供的一个类,用于实现在OpenGL场景中进行对象选择的功能。 使用QOpenGL获取QObjectPicker的步骤如下: 1. 首先,需在程序中引入QOpenGL和QObjectPicker的头文件。 #include <QOpenGL> #include <QObjectPicker> 2. 创建一个QOpenGLWidget的子类,用于在窗口中显示OpenGL场景。 3. 在子类的初始化函数中,创建一个QObjectPicker对象,并将其与渲染目标的OpenGL视图相连接。 4. 基于QObjectPicker对象进行需要的设置,例如设置选择模式、设置射线与场景的相交测试等。 5. 在渲染函数中,使用QOpenGL的功能进行OpenGL渲染操作,并应用QObjectPicker来实现选择操作。 6. 处理选择事件以执行相应的操作。通过检查QObjectPicker的相关信号和槽机制,可以知道是否选中了某个对象,并根据具体需求进行相应的处理。 在使用QOpenGL获取QObjectPicker时,需要注意以下几点: - 确保引入了正确的头文件,并正确连接QObjectPicker对象。 - 根据需要设置QObjectPicker的相关属性,以便实现正确的选择行为。 - 根据实际需求处理选择事件,并进行相应的操作。 - 了解QOpenGLOpenGL的基本原理和功能,以便更好地应用QObjectPicker进行对象选择。 通过以上步骤,我们可以使用QOpenGL获取QObjectPicker并实现在OpenGL场景中进行对象选择的功能。
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值