当前位置: 首页 > ds >正文

在Qt中使用OpenGL显示大量点(点云)

在Qt中高效显示大量点(点云),可以使用QOpenGLWidget结合现代OpenGL技术。

一、基本实现步骤

1. 创建QOpenGLWidget子类

#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>class PointCloudViewer : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Core
{Q_OBJECT
public:explicit PointCloudViewer(QWidget *parent = nullptr);~PointCloudViewer();void setPointCloud(const QVector<QVector3D> &points, const QVector<QVector3D> &colors = {});protected:void initializeGL() override;void resizeGL(int w, int h) override;void paintGL() override;private:QOpenGLShaderProgram *m_program;QOpenGLBuffer m_vbo;QMatrix4x4 m_projection;QVector<QVector3D> m_points;QVector<QVector3D> m_colors;int m_pointCount;
};

2. 实现类方法

PointCloudViewer::PointCloudViewer(QWidget *parent): QOpenGLWidget(parent), m_program(nullptr), m_pointCount(0)
{QSurfaceFormat format;format.setSamples(4);format.setVersion(3, 3);format.setProfile(QSurfaceFormat::CoreProfile);setFormat(format);
}PointCloudViewer::~PointCloudViewer()
{makeCurrent();m_vbo.destroy();delete m_program;doneCurrent();
}void PointCloudViewer::setPointCloud(const QVector<QVector3D> &points, const QVector<QVector3D> &colors)
{m_points = points;m_colors = colors.isEmpty() ? QVector<QVector3D>(points.size(), QVector3D(1,1,1)) : colors;m_pointCount = points.size();if (isValid()) {makeCurrent();// 将数据上传到GPUm_vbo.bind();m_vbo.allocate(m_pointCount * (3 + 3) * sizeof(float));float *data = static_cast<float*>(m_vbo.map(QOpenGLBuffer::WriteOnly));for (int i = 0; i < m_pointCount; ++i) {data[i*6 + 0] = m_points[i].x();data[i*6 + 1] = m_points[i].y();data[i*6 + 2] = m_points[i].z();data[i*6 + 3] = m_colors[i].x();data[i*6 + 4] = m_colors[i].y();data[i*6 + 5] = m_colors[i].z();}m_vbo.unmap();m_vbo.release();doneCurrent();update();}
}void PointCloudViewer::initializeGL()
{initializeOpenGLFunctions();glClearColor(0.1f, 0.1f, 0.1f, 1.0f);// 创建着色器程序m_program = new QOpenGLShaderProgram(this);m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,"#version 330 core\n""layout(location = 0) in vec3 position;\n""layout(location = 1) in vec3 color;\n""uniform mat4 projection;\n""out vec3 fragColor;\n""void main() {\n""    gl_Position = projection * vec4(position, 1.0);\n""    fragColor = color;\n""}");m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,"#version 330 core\n""in vec3 fragColor;\n""out vec4 outColor;\n""void main() {\n""    outColor = vec4(fragColor, 1.0);\n""}");m_program->link();// 创建VBOm_vbo.create();m_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);if (!m_points.isEmpty()) {setPointCloud(m_points, m_colors);}
}void PointCloudViewer::resizeGL(int w, int h)
{m_projection.setToIdentity();m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}void PointCloudViewer::paintGL()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);if (m_pointCount == 0) return;m_program->bind();m_program->setUniformValue("projection", m_projection);m_vbo.bind();glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glPointSize(2.0f);glDrawArrays(GL_POINTS, 0, m_pointCount);m_vbo.release();m_program->release();
}

二、优化大量点云显示

对于非常大的点云(数十万或数百万点),可以考虑以下优化:

1. 使用顶点缓冲对象(VBO)和顶点数组对象(VAO)

cpp

// 在类定义中添加
QOpenGLVertexArrayObject m_vao;// 在initializeGL中初始化VAO
m_vao.create();
m_vao.bind();// 在paintGL中使用VAO
m_vao.bind();
glDrawArrays(GL_POINTS, 0, m_pointCount);
m_vao.release();

2. 使用几何着色器调整点大小(可选)

cpp

// 添加几何着色器
m_program->addShaderFromSourceCode(QOpenGLShader::Geometry,"#version 330 core\n""layout(points) in;\n""layout(triangle_strip, max_vertices = 4) out;\n""uniform mat4 projection;\n""uniform float pointSize;\n""in vec3 fragColor[];\n""out vec3 gColor;\n""void main() {\n""    gColor = fragColor[0];\n""    vec4 pos = gl_in[0].gl_Position;\n""    gl_Position = pos + vec4(-pointSize, -pointSize, 0, 0);\n""    EmitVertex();\n""    gl_Position = pos + vec4(pointSize, -pointSize, 0, 0);\n""    EmitVertex();\n""    gl_Position = pos + vec4(-pointSize, pointSize, 0, 0);\n""    EmitVertex();\n""    gl_Position = pos + vec4(pointSize, pointSize, 0, 0);\n""    EmitVertex();\n""    EndPrimitive();\n""}");

3. 实现LOD(细节层次)渲染

对于极大点云,可以根据视距动态调整显示的点的密度:

cpp

void PointCloudViewer::paintGL()
{// ...其他代码...// 根据距离计算采样率float lodFactor = calculateLODFactor(); // 实现此函数根据相机距离返回0-1值if (lodFactor < 0.3f) {// 高细节:显示所有点glDrawArrays(GL_POINTS, 0, m_pointCount);} else {// 低细节:每N个点显示一个int step = static_cast<int>(1.0f + lodFactor * 10);glDrawArrays(GL_POINTS, 0, m_pointCount / step);}// ...其他代码...
}

4. 使用计算着色器进行点云处理(OpenGL 4.3+)

对于高级应用,可以使用计算着色器在GPU上进行点云处理。

5. 使用顶点缓冲对象(VBO)和顶点数组对象(VAO)实现代码

PointCloudViewer.h

#ifndef POINTCLOUDVIEWER_H
#define POINTCLOUDVIEWER_H#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>class PointCloudViewer : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Core
{Q_OBJECT
public:explicit PointCloudViewer(QWidget *parent = nullptr);~PointCloudViewer();void setPointCloud(const QVector<QVector3D> &points, const QVector<QVector3D> &colors = {});protected:void initializeGL() override;void resizeGL(int w, int h) override;void paintGL() override;private:QOpenGLShaderProgram *m_program;QOpenGLVertexArrayObject m_vao;QOpenGLBuffer m_vbo;QMatrix4x4 m_projection;QVector<QVector3D> m_points;QVector<QVector3D> m_colors;int m_pointCount;bool m_dataUploaded;
};#endif // POINTCLOUDVIEWER_H

PointCloudViewer.cpp

#include "pointcloudviewer.h"
#include <QDebug>PointCloudViewer::PointCloudViewer(QWidget *parent): QOpenGLWidget(parent), m_program(nullptr), m_pointCount(0)
{/*QSurfaceFormat format;format.setSamples(4);format.setVersion(3, 3);// 移除下面这行或改为CompatibilityProfileformat.setProfile(QSurfaceFormat::CoreProfile);//format.setProfile(QSurfaceFormat::CompatibilityProfile);setFormat(format);*/m_dataUploaded = false;m_projection.setToIdentity();
}PointCloudViewer::~PointCloudViewer()
{makeCurrent();m_vbo.destroy();delete m_program;doneCurrent();
}void PointCloudViewer::setPointCloud(const QVector<QVector3D> &points, const QVector<QVector3D> &colors)
{m_points = points;m_colors = colors.isEmpty() ? QVector<QVector3D>(points.size(), QVector3D(1,1,1)) : colors;m_pointCount = points.size();m_dataUploaded = false;if (isValid()) {makeCurrent();// 将数据上传到GPUm_vao.bind();m_vbo.bind();m_vbo.allocate(m_pointCount * (3 + 3) * sizeof(float));float *data = static_cast<float*>(m_vbo.map(QOpenGLBuffer::WriteOnly));for (int i = 0; i < m_pointCount; ++i) {data[i*6 + 0] = m_points[i].x();data[i*6 + 1] = m_points[i].y();data[i*6 + 2] = m_points[i].z();data[i*6 + 3] = m_colors[i].x();data[i*6 + 4] = m_colors[i].y();data[i*6 + 5] = m_colors[i].z();}m_vbo.unmap();m_vbo.release();m_vao.release();m_dataUploaded = true;doneCurrent();update();}
}void PointCloudViewer::initializeGL()
{initializeOpenGLFunctions();glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glEnable(GL_DEPTH_TEST);glEnable(GL_PROGRAM_POINT_SIZE);// 创建VAOm_vao.create();m_vao.bind();// 创建VBOm_vbo.create();m_vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);m_vbo.bind();// 设置顶点属性glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));m_vbo.release();m_vao.release();// 创建着色器程序m_program = new QOpenGLShaderProgram(this);m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,"#version 450 core\n""layout (location=0) in vec3 position;\n""layout (location=1) in vec3 color;\n""uniform mat4 projection;\n""out vec3 fragSetColor;\n""void main() {\n""    gl_Position = projection * vec4(position, 1.0);\n""    fragSetColor = color;\n""    gl_PointSize = 2.0;\n"  // 直接在着色器中设置点大小"}");m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,"#version 150 core\n""in vec3 fragSetColor;\n""out vec4 FragColor;\n""void main() {\n""    FragColor = vec4(fragSetColor, 1.0);\n""}");if(!m_program->link()) {qDebug() << "Shader link error:" << m_program->log();}if (!m_points.isEmpty()) {setPointCloud(m_points, m_colors);}
}void PointCloudViewer::resizeGL(int w, int h)
{m_projection.setToIdentity();m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}void PointCloudViewer::paintGL()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);if (!m_dataUploaded || m_pointCount == 0) return;m_program->bind();m_program->setUniformValue("projection", m_projection);m_vao.bind();glDrawArrays(GL_POINTS, 0, m_pointCount);m_vao.release();m_program->release();
}

三、使用方法

cpp

// 在主窗口中使用
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent)
{PointCloudViewer *viewer = new PointCloudViewer(this);setCentralWidget(viewer);// 生成示例点云QVector<QVector3D> points;QVector<QVector3D> colors;for (int i = 0; i < 100000; ++i) {points.append(QVector3D((float)rand()/RAND_MAX * 2 - 1,(float)rand()/RAND_MAX * 2 - 1,(float)rand()/RAND_MAX * 2 - 1));colors.append(QVector3D((float)rand()/RAND_MAX,(float)rand()/RAND_MAX,(float)rand()/RAND_MAX));}viewer->setPointCloud(points, colors);
}

 

四、注意事项

  1. 对于极大点云(>1百万点),考虑使用点云库如PCL进行预处理

  2. 确保OpenGL上下文版本足够高(至少3.3)

  3. 在渲染前检查点数量,避免空渲染

  4. 考虑添加相机控制和交互功能

  5. 对于专业应用,可能需要实现点云拾取、着色等功能

http://www.xdnf.cn/news/14601.html

相关文章:

  • 136. 只出现一次的数字
  • 算法题(力扣每日一题)—改变一个整数能得到的最大差值
  • Arthas 全面学习指南
  • 动手实践:LangChain流图可视化全解析
  • [从0到1]环境准备--anaconda与pycharm的安装
  • Linux系统firewall-offline-cmd命令在企业网络安全防护中的应用案例分析
  • 图形编辑器基于Paper.js教程29:基于图层的所有矢量图元的填充规则实现
  • 【C++】list容器实现
  • Lighthouse与首屏优化
  • 【看到哪里写到哪里】如何在C中使用多进程设计(1)
  • STM32 开发 - STM32CubeMX 下载芯片支持包、创建 HAL 库工程
  • 牙科医疗设备EMC电磁兼容技术讨论
  • 数列的极限
  • 推荐标注数据标注
  • 【精选】计算机毕业设计基于SpringBoot高校社团管理系统 社团信息维护 活动发布报名 成员审核与公告发布平台源码+论文+PPT+讲解
  • Git(三) Git 分支工作流管理模型探究与实践
  • 电容篇---常见作用
  • Apache Iceberg与Hive集成:分区表篇
  • StarRocks Community Monthly Newsletter (May)
  • JavaScript中Date对象用法详解
  • 深入实践Caffeine+Redis两级缓存架构:从原理到高可用设计
  • 「Linux文件及目录管理」文件及目录操作类命令
  • Grdle版本与Android Gradle Plugin版本, Android Studio对应关系
  • OpenWrt:交叉编译openssl
  • redis缓存的基础知识
  • DBSCAN(Density-Based Spatial Clustering of Applications with Noise)基于密度的聚类方法介绍
  • 移动应用开发实验室web组大一下期末考核题解
  • 【arXiv2024】时间序列|TimesFM-ICF:即插即用!时间序列预测新王者!吊打微调!
  • 如何用ai设计测试
  • WebStorm编辑器侧边栏