7.IO

VTK综合实战Demo:简易模型查看器

把前面学过的全部知识点串起来:文件读取Source → Filter预处理 → 渲染管线 → 鼠标拾取回调 → IO导出保存。

功能清单:

  1. 读取stl模型(Reader属于Source)
  2. Filter做网格清理
  3. 完整渲染交互
  4. 鼠标左键拾取面片,打印面片ID、交点坐标(Observer拾取回调)
  5. 把处理之后的网格保存为vtp文件(Writer)

逻辑链路: stl文件 → vtkSTLReader(Source) → vtkCleanPolyData(Filter) → PolyDataMapper → Actor → Renderer 拾取:鼠标事件 → Observer回调 → vtkCellPicker射线拾取 输出:把处理后的PolyData保存为vtp

#include <vtkSmartPointer.h>
#include <vtkSTLReader.h>
#include <vtkCleanPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkCellPicker.h>
#include <vtkCommand.h>
#include <vtkXMLPolyDataWriter.h>
#include <iostream>

// 拾取回调类,观察者模式
class PickCallback : public vtkCommand
{
public:
    static PickCallback* New()
    {
        return new PickCallback;
    }
    virtual void Execute(vtkObject* caller, unsigned long vtkNotUsed(eventId), void*) override
    {
        vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
        int x, y;
        interactor->GetEventPosition(x, y);

        vtkSmartPointer<vtkCellPicker> picker = vtkSmartPointer<vtkCellPicker>::New();
        picker->SetTolerance(0.0001);

        auto renderer = interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer();
        picker->Pick(x, y, 0, renderer);

        int cellId = picker->GetCellId();
        if(cellId >= 0)
        {
            double pos[3];
            picker->GetPickPosition(pos);
            std::cout << "【拾取成功】CellId=" << cellId
                      << " 交点(" << pos[0] << "," << pos[1] << "," << pos[2] << ")\n";
        }
        else
        {
            std::cout << "【未命中模型】\n";
        }
    }
};


int main()
{
    // ==========1.读取stl文件(Reader,属于Source)==========
    vtkSmartPointer<vtkSTLReader> stlReader = vtkSmartPointer<vtkSTLReader>::New();
    stlReader->SetFileName("model.stl"); //替换成你的stl路径
    stlReader->Update(); //读取解析文件,得到vtkPolyData

    // ==========2.Filter预处理:清理重复点==========
    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
    cleanFilter->SetInputData(stlReader->GetOutput());
    cleanFilter->SetTolerance(1e-4);
    cleanFilter->Update();

    //拿到处理完成后的数据集
    vtkPolyData* outPoly = cleanFilter->GetOutput();

    // ==========3.把处理完的数据保存为vtp文件(Writer)==========
    vtkSmartPointer<vtkXMLPolyDataWriter> vtpWriter = vtkSmartPointer<vtkXMLPolyDataWriter>::New();
    vtpWriter->SetFileName("output.vtp");
    vtpWriter->SetInputData(outPoly);
    vtpWriter->Write(); //Writer用Write,不是Update
    std::cout << "已导出 output.vtp\n";

    // ==========4.渲染管线==========
    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputData(outPoly);

    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper.Get());
    actor->GetProperty()->SetColor(0.4,0.6,0.9);
    actor->GetProperty()->SetEdgeVisibility(true); //打开边线,看三角面片轮廓

    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
    renderer->AddActor(actor);
    renderer->SetBackground(0.1,0.1,0.1);

    vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    renderWindow->SetSize(800,600);

    vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
    interactor->SetRenderWindow(renderWindow);

    vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
    interactor->SetInteractorStyle(style);

    // ==========5.绑定鼠标左键拾取回调(Observer)==========
    vtkSmartPointer<PickCallback> pickCb = vtkSmartPointer<PickCallback>::New();
    interactor->AddObserver(vtkCommand::LeftButtonPressEvent, pickCb);

    interactor->Initialize();
    interactor->Start();

    return 0;
}

逐段复盘,对应前面全部知识点

  1. Source(vtkSTLReader) SetFileName设置参数 → Update()执行解析,生成vtkPolyData。

    Reader就是读文件的Source,没有输入,输出数据集。

  2. Filter(vtkCleanPolyData) 输入是reader输出,做网格清理;Update()执行算法,输出新PolyData。

    Filter不会修改原始reader的数据,输出全新一份。

  3. IO Writer vtkXMLPolyDataWriter,SetInputData()传入内存网格,Write()写磁盘。

    注意区分:Source/Filter调用Update();Writer调用Write()。

  4. Mapper + Actor + Renderer Mapper把数据集送给Actor;Actor的Property只控制渲染外观,不改内存网格;打开边线方便观察三角面片。

  5. 交互Interactor + InteractorStyle TrackballCamera,旋转缩放本质是相机变化,PolyData点坐标完全不变。

  6. Observer观察者 + vtkCellPicker拾取 AddObserver监听鼠标左键事件;回调内部做射线拾取;射线起点是相机位置,拿到CellId与交点。

    cellId=-1代表没有命中物体。

运行之后你可以观察到的现象

  1. 程序启动,窗口显示stl模型,能旋转缩放平移;
  2. 控制台打印“已导出 output.vtp”,可以用ParaView打开这个vtp查看网格;
  3. 鼠标左键点击模型,控制台打印面片ID和三维交点;点空白输出未命中;
  4. stl里面只有几何,导出vtp之后,如果你给PolyData加上PointData/CellData属性,vtp会完整保存这些数组,stl做不到。

工业软件视角下,这个Demo缺什么?

这是纯VTK程序,还没有接入OCCT。 真实工业软件流程: stp(STEP)文件 → OCCT读取BRep实体 → OCCT三角化 tessellate → 产出vtkPolyData → 送入VTK这套管线

VTK只负责后面这一整套渲染、拾取、Filter处理、网格IO;STEP实体读写交给OCCT。

VTK基础全部核心概念总览(快速复盘)

  1. 数据层:vtkDataSet(vtkPolyData重点),Points、Cell、PointData、CellData;
  2. 管线层:Source → Filter → Mapper → Actor → Renderer → RenderWindow;
  3. 延迟计算:SetXXX改参数,Update()触发算法;Filter链式会向上更新上游;
  4. 渲染外观:vtkProperty,只改显示,不改内存数据;和Filter做区分;
  5. 交互拾取:Interactor、InteractorStyle;拾取射线从相机出发;CellPicker拿面片ID;
  6. IO:Reader(Source)、Writer;stl只存几何,vtp存几何+属性;VTK不处理stp;
  7. 事件:vtkCommand观察者模式,鼠标事件、进度事件。
posted @ 2026-08-24 16:11  Drunker•L  阅读(13)  评论(0)    收藏  举报