VTK+OCC显示CAD模型

发布时间 2023-07-22 13:48:24作者: 山岚2013

VTK是一款十分优秀的可视化套件,开源且功能强大,基本上可以满足有限元领域的全部可视化需求。遗憾的是,VTK不支持CAD模型(如igs、stp格式的模型)的显示。

在网上搜索后可以发现,在不花钱的情况下,想要显示和处理CAD模型,基本上都得使用OpenCasCade,即OCC。OCC有自己的可视化系统,也可以集成在Qt中,网上也有相关的Demo。但对我而已,OCC自己的可视化系统还是太复杂了。为了一个简单的显示功能,专门去深入学习OCC的可视化系统还是太难为人了。如果能直接用VTK显示OCC的模型就好了。

万幸,OCC也知道很多人和我有一样的需求,在6.8版本开发了VIS(VTK Integration Services)功能,之后的版本就可以使用VTK进行模型的可视化了。

为了使用VIS功能,编译OCC的时候需要选择USE_VTK的选项,编译完成后,将生成TKIVtk、TKIVtkDraw的动态库和静态库。如果编译路径下有这两个库,说明VIS的功能是编译成功了。

编写一个最小案例看看显示效果。

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(occvtk LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(VTK_DIR "C:/Program Files/VTK/lib/cmake/vtk-9.1")

find_package(VTK COMPONENTS 
 vtkCommonCore
 vtkFiltersCore
 vtkCommonDataModel
 vtkInteractionStyle
 vtkRenderingContextOpenGL2
 vtkRenderingCore
 vtkRenderingFreeType
 vtkRenderingGL2PSOpenGL2
 vtkRenderingOpenGL2
 QUIET
)

include_directories("C:/Program Files/OCCT/inc")
link_directories("C:/Program Files/OCCT/win64/vc14/lib")

add_executable(make_box make_box.cpp)
target_link_libraries(make_box 
    TKPrim TKIVtk TKMath TKernel 
    TKTopAlgo TKGeomAlgo TKV3d 
    ${VTK_LIBRARIES})

make_box.cpp 文件

#include <BRepPrimAPI_MakeBox.hxx>
#include <IVtkTools_ShapeDataSource.hxx>
#include <vtkType.h>
#include <vtkAutoInit.h>
#include <vtkRenderWindow.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkPolyDataMapper.h>
#include <vtkInteractorStyleTrackballCamera.h>

VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)

int main()
{
    BRepPrimAPI_MakeBox mkBox(1., 2., 3);
    const TopoDS_Shape& shape = mkBox.Shape();

    vtkNew<IVtkTools_ShapeDataSource> occSource;
    occSource->SetShape(new IVtkOCC_Shape(shape));

    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputConnection(occSource->GetOutputPort());
    
    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);
    
    vtkNew<vtkRenderer> ren;
    ren->AddActor(actor);

    vtkNew<vtkRenderWindow> renWin;
    renWin->AddRenderer(ren);
    renWin->SetSize(640, 640);

    vtkNew<vtkInteractorStyleTrackballCamera> istyle;
    vtkNew<vtkRenderWindowInteractor> iren;

    iren->SetRenderWindow(renWin);
    iren->SetInteractorStyle(istyle);

    renWin->Render();
    iren->Start();

    return 0;
}

显示效果如下:

看着还不错。但模型中有一堆白线,有没有什么方法去掉呢。

经过搜索,发现这个白线叫什么iso line,可以通过IVtkOCC_ShapeMesher去掉。

查看上面的代码,是没有涉及到IVtkOCC_ShapeMesher的。继续查阅文档,发现VIS有两种接口,一种是high-level API,就是上面那种,另一种是low-level API,可以控制的东西更多一些。

现在试试low-level API。

在CMakeLists.txt文件中添加:


add_executable(make_box2 make_box2.cpp)
target_link_libraries(make_box2 
    TKPrim TKIVtk TKMath TKernel 
    TKTopAlgo TKGeomAlgo TKV3d 
    ${VTK_LIBRARIES})

编写make_box2.cpp文件:

#include <BRepPrimAPI_MakeBox.hxx>
#include <IVtkVTK_ShapeData.hxx>
#include <IVtkOCC_ShapeMesher.hxx>
#include <vtkType.h>
#include <vtkAutoInit.h>
#include <vtkRenderWindow.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkPolyDataMapper.h>
#include <vtkInteractorStyleTrackballCamera.h>

VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)

int main()
{
    BRepPrimAPI_MakeBox mkBox(1., 2., 3);
    const TopoDS_Shape& shape = mkBox.Shape();

    IVtkOCC_Shape::Handle aShapeImpl = new IVtkOCC_Shape(shape);
    IVtkVTK_ShapeData::Handle aDataImpl = new IVtkVTK_ShapeData();
    IVtk_IShapeMesher::Handle aMesher = new IVtkOCC_ShapeMesher(0.0001, 12.0*M_PI/180, 0, 0);
    aMesher->Build(aShapeImpl, aDataImpl);
    vtkPolyData* aPolyData = aDataImpl->getVtkPolyData();

    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputData(aPolyData);
    
    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);
    
    vtkNew<vtkRenderer> ren;
    ren->AddActor(actor);

    vtkNew<vtkRenderWindow> renWin;
    renWin->AddRenderer(ren);
    renWin->SetSize(960, 800);

    vtkNew<vtkInteractorStyleTrackballCamera> istyle;
    vtkNew<vtkRenderWindowInteractor> iren;

    iren->SetRenderWindow(renWin);
    iren->SetInteractorStyle(istyle);

    renWin->Render();
    iren->Start();

    return 0;
}

这一次的显示效果如下:

可以看到,白线没有了。

试一下显示stp文件。

在CMakeLists.txt文件中添加:

add_executable(import_step2 import_step2.cpp)
target_link_libraries(import_step2 
    TKSTEP TKIVtk TKV3d 
    TKGeomAlgo TKMath TKXSBase TKernel 
    ${VTK_LIBRARIES})

import_step2.cpp文件:

#include <STEPControl_Reader.hxx>
#include <Standard_Integer.hxx>
#include <TopoDS_Shape.hxx>
#include <IFSelect_ReturnStatus.hxx>
#include <IFSelect_PrintCount.hxx>
#include <IVtkVTK_ShapeData.hxx>
#include <IVtkOCC_ShapeMesher.hxx>
#include <vtkType.h>
#include <vtkAutoInit.h>
#include <vtkRenderWindow.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkPolyDataMapper.h>
#include <vtkInteractorStyleTrackballCamera.h>

VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)

int main()
{
    STEPControl_Reader reader;
    IFSelect_ReturnStatus stat = reader.ReadFile("assembly_solid.stp");
    IFSelect_PrintCount mode = IFSelect_CountByItem;
    Standard_Integer NbRoots = reader.NbRootsForTransfer();
    Standard_Integer num = reader.TransferRoots();
    Standard_Integer NbTrans = reader.TransferRoots();
    TopoDS_Shape result = reader.OneShape();
    TopoDS_Shape shape = reader.Shape();

    Handle_IVtkOCC_Shape aShapeImpl = new IVtkOCC_Shape(shape);
    Handle_IVtkVTK_ShapeData aDataImpl = new IVtkVTK_ShapeData();
    Handle_IVtk_IShapeMesher aMesher = new IVtkOCC_ShapeMesher(0.0001, 12.0 * M_PI / 180, 0, 0);
    aMesher->Build(aShapeImpl, aDataImpl);
    vtkPolyData* aPolyData = aDataImpl->getVtkPolyData();

    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputData(aPolyData);

    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);

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

    vtkNew<vtkRenderWindow> renWin;
    renWin->AddRenderer(ren);
    renWin->SetSize(960, 800);

    vtkNew<vtkInteractorStyleTrackballCamera> istyle;
    vtkNew<vtkRenderWindowInteractor> iren;

    iren->SetRenderWindow(renWin);
    iren->SetInteractorStyle(istyle);

    renWin->Render();
    iren->Start();

    return 0;
}

这个效果就不是很理想了,弧面被离散成了很多小面片,不是很光滑。

继续查看OCC的文档看看有什么解决方法,发现可以使用vtkPolyDataNormals进行光滑。试一下效果。

在CMakeLists.txt文件中添加:

add_executable(import_step3 import_step3.cpp)
target_link_libraries(import_step3 
    TKSTEP TKIVtk TKV3d TKGeomAlgo 
    TKMath TKXSBase TKernel 
    ${VTK_LIBRARIES})

import_step3.cpp文件:

#include <STEPControl_Reader.hxx>
#include <Standard_Integer.hxx>
#include <TopoDS_Shape.hxx>
#include <IFSelect_ReturnStatus.hxx>
#include <IFSelect_PrintCount.hxx>
#include <IVtkVTK_ShapeData.hxx>
#include <IVtkOCC_ShapeMesher.hxx>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkPolyDataNormals.h>
#include <vtkType.h>
#include <vtkAutoInit.h>

VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)

int main()
{
    STEPControl_Reader reader;
    IFSelect_ReturnStatus stat = reader.ReadFile("assembly_solid.stp");
    IFSelect_PrintCount mode = IFSelect_CountByItem;
    Standard_Integer NbRoots = reader.NbRootsForTransfer();
    Standard_Integer num = reader.TransferRoots();
    Standard_Integer NbTrans = reader.TransferRoots();
    TopoDS_Shape result = reader.OneShape();
    TopoDS_Shape shape = reader.Shape();

    Handle_IVtkOCC_Shape aShapeImpl = new IVtkOCC_Shape(shape);
    Handle_IVtkVTK_ShapeData aDataImpl = new IVtkVTK_ShapeData();
    Handle_IVtk_IShapeMesher aMesher = new IVtkOCC_ShapeMesher(0.0001, 3.0 * M_PI / 180, 0, 0);
    aMesher->Build(aShapeImpl, aDataImpl);
    vtkPolyData* aPolyData = aDataImpl->getVtkPolyData();

    vtkNew<vtkPolyDataNormals> normalGenerator;
    normalGenerator->SetInputData(aPolyData);
    normalGenerator->Update();

    vtkNew<vtkPolyDataMapper> mapper;
    mapper->SetInputConnection(normalGenerator->GetOutputPort());

    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);

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

    vtkNew<vtkRenderWindow> renWin;
    renWin->AddRenderer(ren);
    renWin->SetSize(960, 800);

    vtkNew<vtkInteractorStyleTrackballCamera> istyle;
    vtkNew<vtkRenderWindowInteractor> iren;

    iren->SetRenderWindow(renWin);
    iren->SetInteractorStyle(istyle);

    renWin->Render();
    iren->Start();

    return 0;
}

这一次效果就好了很多,基本已经可以满足常规需求了。如果对显示效果还有更高要求,可以进一步修改IVtkOCC_ShapeMesher里的参数。