使用Qt、VTK、PCL显示PCD点云时视角旋转后显示不全的问题求助
问题:Qt+VTK+PCL旋转视角时点云显示不全
运行环境:Qt 5.15.2、PCL 1.14.1、VTK 9.3、Visual Studio 2022、Windows 10
现象:在Qt中通过VTK+PCL显示随机生成的点云(或其他点云模型)时,初始显示正常,但旋转视角后点云显示不全;而C++控制台+PCL环境下旋转视角可完整显示。相关代码如下:
#include <QtWidgets/QApplication> #include <QtWidgets/QMainWindow> #include <QtCore/QVariant> #include <QtWidgets/QApplication> #include <QtWidgets/QMainWindow> #include <QtWidgets/QMenuBar> #include <QtWidgets/QStatusBar> #include <QtWidgets/QToolBar> #include <QtWidgets/QWidget> #include <qvtkopenglnativewidget.h> #include <pcl/io/pcd_io.h> #include <pcl/io/ply_io.h> #include <pcl/visualization/pcl_visualizer.h> #include <vtkGenericOpenGLRenderWindow.h> QT_BEGIN_NAMESPACE class Ui_QtPCLDemoClass { public: QWidget* centralWidget; QVTKOpenGLNativeWidget* openGLWidget; QMenuBar* menuBar; QToolBar* mainToolBar; QStatusBar* statusBar; void setupUi(QMainWindow* QtPCLDemoClass) { if (QtPCLDemoClass->objectName().isEmpty()) QtPCLDemoClass->setObjectName(QString::fromUtf8("QtPCLDemoClass")); QtPCLDemoClass->resize(1202, 844); centralWidget = new QWidget(QtPCLDemoClass); centralWidget->setObjectName(QString::fromUtf8("centralWidget")); openGLWidget = new QVTKOpenGLNativeWidget(centralWidget); openGLWidget->setObjectName(QString::fromUtf8("openGLWidget")); openGLWidget->setGeometry(QRect(10, 10, 1081, 691)); QtPCLDemoClass->setCentralWidget(centralWidget); menuBar = new QMenuBar(QtPCLDemoClass); menuBar->setObjectName(QString::fromUtf8("menuBar")); menuBar->setGeometry(QRect(0, 0, 1202, 23)); QtPCLDemoClass->setMenuBar(menuBar); mainToolBar = new QToolBar(QtPCLDemoClass); mainToolBar->setObjectName(QString::fromUtf8("mainToolBar")); QtPCLDemoClass->addToolBar(Qt::TopToolBarArea, mainToolBar); statusBar = new QStatusBar(QtPCLDemoClass); statusBar->setObjectName(QString::fromUtf8("statusBar")); QtPCLDemoClass->setStatusBar(statusBar); retranslateUi(QtPCLDemoClass); QMetaObject::connectSlotsByName(QtPCLDemoClass); } // setupUi void retranslateUi(QMainWindow* QtPCLDemoClass) { QtPCLDemoClass->setWindowTitle(QCoreApplication::translate("QtPCLDemoClass", "QtPCLDemo", nullptr)); } // retranslateUi }; namespace Ui { class QtPCLDemoClass : public Ui_QtPCLDemoClass {}; } // namespace Ui QT_END_NAMESPACE class QtPCLDemo : public QMainWindow { Q_OBJECT public: QtPCLDemo(QWidget* parent = nullptr); private: Ui::QtPCLDemoClass ui; pcl::PointCloud<pcl::PointXYZ>::Ptr cloud; boost::shared_ptr<pcl::visualization::PCLVisualizer> view; }; QtPCLDemo::QtPCLDemo(QWidget* parent) : QMainWindow(parent) { ui.setupUi(this); vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New(); renderWindow->AddRenderer(renderer); view.reset(new pcl::visualization::PCLVisualizer(renderer, renderWindow, "viewer", false)); view->setupInteractor(ui.openGLWidget->interactor(), ui.openGLWidget->renderWindow()); ui.openGLWidget->setRenderWindow(view->getRenderWindow()); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud( new pcl::PointCloud<pcl::PointXYZ>); srand(static_cast<unsigned int>(time(0))); cloud->width = 100; cloud->height = 10; cloud->points.resize(cloud->width * cloud->height); for (std::size_t i = 0; i < cloud->points.size(); ++i) { cloud->points[i].x = 1024.0f * rand() / (RAND_MAX + 1.0f); cloud->points[i].y = 1024.0f * rand() / (RAND_MAX + 1.0f); cloud->points[i].z = 1024.0f * rand() / (RAND_MAX + 1.0f); } pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZ> fildColor(cloud, "z"); view->addPointCloud(cloud, fildColor, "cloud"); view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "cloud"); view->spin(); ui.openGLWidget->update(); } int main(int argc, char *argv[]) { QApplication a(argc, argv); QtPCLDemo w; w.show(); return a.exec(); } #include "main.moc"
原因分析
- 阻塞式渲染循环冲突:
view->spin()是PCL Visualizer的阻塞式渲染方法,在Qt主线程中调用会抢占Qt的事件循环,导致窗口交互、视图更新逻辑异常,进而引发点云渲染不全。 - 渲染窗口绑定顺序错误:代码中先绑定交互器再设置渲染窗口,导致VTK与Qt控件的渲染上下文同步异常。
- 视锥体未正确初始化:未调用相机重置方法,初始视锥体参数可能无法覆盖全部点云,旋转后部分点被裁剪。
解决方案
核心修改点
- 移除阻塞式的
view->spin(),改用Qt定时器驱动VTK渲染,融入Qt事件循环。 - 调整渲染窗口与交互器的绑定顺序,确保Qt控件正确接管VTK渲染上下文。
- 添加相机重置操作,让VTK自动计算覆盖全部点云的视锥体参数。
修改后的构造函数代码:
QtPCLDemo::QtPCLDemo(QWidget* parent) : QMainWindow(parent) { ui.setupUi(this); vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New(); renderWindow->AddRenderer(renderer); view.reset(new pcl::visualization::PCLVisualizer(renderer, renderWindow, "viewer", false)); // 先给Qt控件设置渲染窗口,再绑定交互器 ui.openGLWidget->setRenderWindow(renderWindow); view->setupInteractor(ui.openGLWidget->interactor(), ui.openGLWidget->renderWindow()); // 生成随机点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud( new pcl::PointCloud<pcl::PointXYZ>); srand(static_cast<unsigned int>(time(0))); cloud->width = 100; cloud->height = 10; cloud->points.resize(cloud->width * cloud->height); for (std::size_t i = 0; i < cloud->points.size(); ++i) { cloud->points[i].x = 1024.0f * rand() / (RAND_MAX + 1.0f); cloud->points[i].y = 1024.0f * rand() / (RAND_MAX + 1.0f); cloud->points[i].z = 1024.0f * rand() / (RAND_MAX + 1.0f); } // 添加点云并设置属性 pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZ> fildColor(cloud, "z"); view->addPointCloud(cloud, fildColor, "cloud"); view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "cloud"); // 重置相机,确保所有点云在视锥体内 view->resetCamera(); // 用Qt定时器触发渲染,替代阻塞的spin() QTimer* renderTimer = new QTimer(this); connect(renderTimer, &QTimer::timeout, this, [this]() { ui.openGLWidget->renderWindow()->Render(); }); renderTimer->start(16); // 约60fps的刷新频率 }
额外注意事项
- 避免在Qt主线程中执行任何阻塞式操作,所有渲染、交互逻辑必须适配Qt的事件循环机制。
- 若使用自定义点云模型,添加后同样需要调用
resetCamera()确保视锥体覆盖全部点云。
内容的提问来源于stack exchange,提问作者colin
相关产品推荐
相关产品推荐

