QT中使用QVTKOpenGLNativeWidget显示三维图形(VTK9.x以上版本)

1.VTK 版本演进与qt插件支持变化

  • VTK ≤ 7.x :官方提供**QVTKWidget 插件** ,需手动将 QVTKWidgetPlugin.dll/lib 放入 Qt Designer 的插件目录 1
  • VTK 8.2 之前 :推荐使用**QVTKOpenGLWidget** 。
  • VTK 8.2 及之后 :引入 QVTKOpenGLNativeWidget,替代旧版插件 。
  • VTK 9.0+彻底移除 QVTKWidget,仅保留 QVTKOpenGLNativeWidget不再提供 Qt Designer 插件
  • 使用QVTKOpenGLNativeWidget的更加方便,无需在Qt Designer插件再添加相应的插件目录
  • 此外无需在qt designer中在将widget提升为QVTKOpenGLNativeeWidget,
  • 以上两种方式都不便于代码的移植
  • 使用**QVTKOpenGLNativeWidget可以直接将vtk显示的窗口初始化后,直接通过**
  • qt addwidget接口添加到显示界面即可。

2.QVTKOpenGLNativeWidget使用方法

首先通过cmake配置所示包含文件以及链接库

包含文件

链接库

链接库主要文件如下

E:\NativeThirdParty\VTK_9_3\lib\vtkGUISupportQt-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkInteractionWidgets-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkRenderingOpenGL2-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkRenderingHyperTreeGrid-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkRenderingUI-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkglew-9.3.lib

opengl32.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkRenderingContext2D-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkIOImage-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkImagingCore-9.3.lib

C:\Qt\5.15.2\msvc2019_64\lib\Qt5OpenGL.lib

C:\Qt\5.15.2\msvc2019_64\lib\Qt5Widgets.lib

C:\Qt\5.15.2\msvc2019_64\lib\Qt5Gui.lib

C:\Qt\5.15.2\msvc2019_64\lib\Qt5Core.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkRenderingCore-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkFiltersSources-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkFiltersGeneral-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkFiltersCore-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkCommonExecutionModel-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkCommonDataModel-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkCommonTransforms-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkCommonMisc-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkCommonMath-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkCommonCore-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtksys-9.3.lib

ws2_32.lib

dbghelp.lib

psapi.lib

E:\NativeThirdParty\VTK_9_3\lib\vtktoken-9.3.lib

E:\NativeThirdParty\VTK_9_3\lib\vtkkissfft-9.3.lib

kernel32.lib

user32.lib

gdi32.lib

winspool.lib

shell32.lib

ole32.lib

oleaut32.lib

uuid.lib

comdlg32.lib

advapi32.lib

预编译定义

WIN32

_WINDOWS

NDEBUG

kiss_fft_scalar=double

KISSFFT_DLL_IMPORT=1

QT_OPENGL_LIB

QT_WIDGETS_LIB

QT_GUI_LIB

QT_CORE_LIB

QT_NO_DEBUG

CMAKE_INTDIR="RelWithDebInfo"

c++ 代码

cpp 复制代码
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include <QVTKOpenGLNativeWidget.h>
#include <vtkActor.h>
#include <vtkDataSetMapper.h>
#include <vtkDoubleArray.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkPointData.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>

#include <QApplication>
#include <QDockWidget>
#include <QGridLayout>
#include <QLabel>
#include <QMainWindow>
#include <QPointer>
#include <QPushButton>
#include <QVBoxLayout>

#include <cmath>
#include <cstdlib>
#include <random>

namespace
{
/**
 * Deform the sphere source using a random amplitude and modes and render it in
 * the window
 *
 * @param sphere the original sphere source
 * @param mapper the mapper for the scene
 * @param window the window to render to
 * @param randEng the random number generator engine
 */
void Randomize(vtkSphereSource* sphere, vtkMapper* mapper, vtkGenericOpenGLRenderWindow* window,
  std::mt19937& randEng);
} // namespace

int main(int argc, char* argv[])
{
  QSurfaceFormat::setDefaultFormat(QVTKOpenGLNativeWidget::defaultFormat());

  QApplication app(argc, argv);

  // main window
  QMainWindow mainWindow;
  mainWindow.resize(1200, 900);

  // control area
  QDockWidget controlDock;
  mainWindow.addDockWidget(Qt::LeftDockWidgetArea, &controlDock);

  QLabel controlDockTitle("Control Dock");
  controlDockTitle.setMargin(20);
  controlDock.setTitleBarWidget(&controlDockTitle);

  QPointer<QVBoxLayout> dockLayout = new QVBoxLayout();
  QWidget layoutContainer;
  layoutContainer.setLayout(dockLayout);
  controlDock.setWidget(&layoutContainer);

  QPushButton randomizeButton;
  randomizeButton.setText("Randomize");
  dockLayout->addWidget(&randomizeButton);

  // render area
  QPointer<QVTKOpenGLNativeWidget> vtkRenderWidget = new QVTKOpenGLNativeWidget();g

//将生成的QVTKOpenGLNativeWidget设置为mainwindow主窗口部分
  mainWindow.setCentralWidget(vtkRenderWidget);  
  // VTK part
  vtkNew<vtkGenericOpenGLRenderWindow> window;
  vtkRenderWidget->setRenderWindow(window.Get());

  vtkNew<vtkSphereSource> sphere;
  sphere->SetRadius(1.0);
  sphere->SetThetaResolution(100);
  sphere->SetPhiResolution(100);

  vtkNew<vtkDataSetMapper> mapper;
  mapper->SetInputConnection(sphere->GetOutputPort());

  vtkNew<vtkActor> actor;
  actor->SetMapper(mapper);
  actor->GetProperty()->SetEdgeVisibility(true);
  actor->GetProperty()->SetRepresentationToSurface();

  vtkNew<vtkRenderer> renderer;
  renderer->AddActor(actor);

  window->AddRenderer(renderer);

  // setup initial status
  std::mt19937 randEng(0);
  ::Randomize(sphere, mapper, window, randEng);

  // connect the buttons
  QObject::connect(&randomizeButton, &QPushButton::released,
    [&]() { ::Randomize(sphere, mapper, window, randEng); });

  mainWindow.show();

  return app.exec();
}

namespace
{
void Randomize(vtkSphereSource* sphere, vtkMapper* mapper, vtkGenericOpenGLRenderWindow* window,
  std::mt19937& randEng)
{
  // generate randomness
  double randAmp = 0.2 + ((randEng() % 1000) / 1000.0) * 0.2;
  double randThetaFreq = 1.0 + (randEng() % 9);
  double randPhiFreq = 1.0 + (randEng() % 9);

  // extract and prepare data
  sphere->Update();
  vtkSmartPointer<vtkPolyData> newSphere;
  newSphere.TakeReference(sphere->GetOutput()->NewInstance());
  newSphere->DeepCopy(sphere->GetOutput());
  vtkNew<vtkDoubleArray> height;
  height->SetName("Height");
  height->SetNumberOfComponents(1);
  height->SetNumberOfTuples(newSphere->GetNumberOfPoints());
  newSphere->GetPointData()->AddArray(height);

  // deform the sphere
  for (int iP = 0; iP < newSphere->GetNumberOfPoints(); iP++)
  {
    double pt[3] = { 0.0 };
    newSphere->GetPoint(iP, pt);
    double theta = std::atan2(pt[1], pt[0]);
    double phi = std::atan2(pt[2], std::sqrt(std::pow(pt[0], 2) + std::pow(pt[1], 2)));
    double thisAmp = randAmp * std::cos(randThetaFreq * theta) * std::sin(randPhiFreq * phi);
    height->SetValue(iP, thisAmp);
    pt[0] += thisAmp * std::cos(theta) * std::cos(phi);
    pt[1] += thisAmp * std::sin(theta) * std::cos(phi);
    pt[2] += thisAmp * std::sin(phi);
    newSphere->GetPoints()->SetPoint(iP, pt);
  }
  newSphere->GetPointData()->SetScalars(height);

  // reconfigure the pipeline to take the new deformed sphere
  mapper->SetInputDataObject(newSphere);
  mapper->SetScalarModeToUsePointData();
  mapper->ColorByArrayComponent("Height", 0);
  window->Render();
}
} // namespace

结果显示