You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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"

原因分析

  1. 阻塞式渲染循环冲突:view->spin()是PCL Visualizer的阻塞式渲染方法,在Qt主线程中调用会抢占Qt的事件循环,导致窗口交互、视图更新逻辑异常,进而引发点云渲染不全。
  2. 渲染窗口绑定顺序错误:代码中先绑定交互器再设置渲染窗口,导致VTK与Qt控件的渲染上下文同步异常。
  3. 视锥体未正确初始化:未调用相机重置方法,初始视锥体参数可能无法覆盖全部点云,旋转后部分点被裁剪。

解决方案

核心修改点

  1. 移除阻塞式的view->spin(),改用Qt定时器驱动VTK渲染,融入Qt事件循环。
  2. 调整渲染窗口与交互器的绑定顺序,确保Qt控件正确接管VTK渲染上下文。
  3. 添加相机重置操作,让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 06:05:59