8.几何处理Filter实战

第七部分:几何处理Filter实战(工业软件高频)

前面已经懂Filter通用机制:输入数据集,输出新数据集,Update触发计算。 这里全部是做三维CAD后处理、模型预览、仿真前处理最常用的一批Filter,讲清楚用途、典型场景、坑点,附带最简代码片段。

分为4大类:法向量处理、裁剪切片、网格合并提取、网格简化平滑。

前置提醒: 大部分Filter接收 vtkPolyData;输入输出都是PolyData; SetInputData() 接入上游,Update()执行; Filter输出是全新数据,原始输入不会被修改。

1、vtkPolyDataNormals 计算法向量(极其高频)

作用

计算面片法向量、点法向量,存入PolyData的PointData / CellData数组。

为什么工业软件必须用

  1. 没有法向量,光照渲染异常:模型发黑、半边黑半边亮、反光错乱。

  2. 很多其它Filter依赖法向量:裁剪、平滑。

    OCCT三角化输出的网格,经常不带法向量,导入VTK之后模型黑乎乎,第一件事就是跑这个Filter。

#include <vtkPolyDataNormals.h>

vtkSmartPointer<vtkPolyDataNormals> normalsFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
normalsFilter->SetInputData(rawPolyData);

normalsFilter->SetComputePointNormals(true);  //计算点法向量(平滑着色用)
normalsFilter->SetComputeCellNormals(true);   //计算面片法向量(平面着色)
normalsFilter->SetFlipNormals(false);         //是否翻转法向量(模型里外颠倒时打开)
normalsFilter->SetConsistency(true);          //统一面片环绕顺序,修复法向量混乱

normalsFilter->Update();

//输出:带法向量的PolyData,送给mapper渲染
vtkPolyData* outWithNormal = normalsFilter->GetOutput();

常见坑

  • CAD导出网格法向量方向颠倒,模型内部朝外,画面漆黑:打开 SetFlipNormals(true)。
  • 模型面片绕向混乱,一部分亮一部分黑:打开 SetConsistency(true)。

2、vtkClipPolyData 平面裁剪模型

场景:剖切查看模型内部结构,工业软件剖切视图。 用一个平面,把模型切成两部分,可以保留其中一侧,扔掉另一侧。

需要搭配 vtkPlane 定义裁剪平面。

#include <vtkClipPolyData.h>
#include <vtkPlane.h>

//定义裁剪平面:过原点,法向量沿X轴
vtkSmartPointer<vtkPlane> clipPlane = vtkSmartPointer<vtkPlane>::New();
clipPlane->SetOrigin(0,0,0);    //平面经过的点
clipPlane->SetNormal(1,0,0);    //平面法向量

vtkSmartPointer<vtkClipPolyData> clipFilter = vtkSmartPointer<vtkClipPolyData>::New();
clipFilter->SetInputData(spherePoly);
clipFilter->SetClipFunction(clipPlane.Get());

clipFilter->SetInsideOut(false); //false:保留法向量正方向一侧;true取反,保留另一侧
clipFilter->Update();

//输出被裁剪之后的模型,会自动生成新的切面三角面片
vtkPolyData* clipOut = clipFilter->GetOutput();

关键点

  1. 裁剪会生成新的三角面片,就是切开之后的切面;点、cell数量会变化。
  2. SetInsideOut() 控制保留哪一半,做剖切视图经常切换这个开关。
  3. 输入需要有法向量,否则裁剪效果异常。

注意:vtkClipPolyData只对PolyData(表面网格)生效;体网格用vtkClipDataSet。


3、vtkCutter 切片(获取截面线)

和Clip区分,很多人搞混:

  • vtkClipPolyData:切掉一半模型,剩下实体面片。
  • vtkCutter:平面和模型相交,只拿到相交的线(截面轮廓线),不保留两侧模型。

场景:获取零件截面轮廓,看截面形状。

#include <vtkCutter.h>

vtkSmartPointer<vtkPlane> cutPlane = vtkSmartPointer<vtkPlane>::New();
cutPlane->SetOrigin(0,0,0);
cutPlane->SetNormal(0,1,0);

vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
cutter->SetInputData(polyData);
cutter->SetCutFunction(cutPlane.Get());
cutter->Update();

//输出是vtkPolyData,里面全是线单元(vtkLines),没有三角面片
vtkPolyData* cutResult = cutter->GetOutput();

对比记忆

  1. Clip:切割实体,输出三角面片(半个模型)
  2. Cutter:求交,输出相交线(截面轮廓)

4、vtkAppendPolyData 网格合并

把多个独立的vtkPolyData,合并成一个vtkPolyData。 场景:一个装配体,多个零件三角化之后得到多份PolyData,合并成一个整体用于拾取、导出。

#include <vtkAppendPolyData.h>

vtkSmartPointer<vtkAppendPolyData> appendFilter = vtkSmartPointer<vtkAppendPolyData>::New();

appendFilter->AddInputData(polyA);
appendFilter->AddInputData(polyB);
appendFilter->AddInputData(polyC);

appendFilter->Update();

//全部合并为一个PolyData
vtkPolyData* merged = appendFilter->GetOutput();

⚠️重要坑:

Append只是简单把点、面片拼在一起,不会自动合并重合点。 两个零件贴合的地方,重合点会重复存在。 合并完之后,建议后面串一级 vtkCleanPolyData,用来合并重复点。

polyA + polyB → vtkAppendPolyData → vtkCleanPolyData → 最终合并网格

5、vtkExtractSurface 提取体网格外表面

输入:vtkUnstructuredGrid(有限元四面体体网格) 输出:vtkPolyData,仅仅提取物体外表三角面,扔掉内部单元。

场景:仿真求解器输出体网格,不想渲染内部单元,只渲染外壳。

#include <vtkExtractSurface.h>

vtkSmartPointer<vtkExtractSurface> extractSurf = vtkSmartPointer<vtkExtractSurface>::New();
extractSurf->SetInputData(unstructuredGrid);
extractSurf->Update();

vtkPolyData* surfacePoly = extractSurf->GetOutput();

6、网格简化 vtkQuadricDecimation(减面)

工业场景:CAD模型三角化之后面片数量巨大,几十万、上百万三角面,渲染卡顿。使用这个Filter做网格简化,减少三角面片数量,尽量保留外形。

#include <vtkQuadricDecimation.h>

vtkSmartPointer<vtkQuadricDecimation> decim = vtkSmartPointer<vtkQuadricDecimation>::New();
decim->SetInputData(rawPoly);
decim->SetTargetReduction(0.6); //目标减少60%面片;保留40%
decim->Update();

vtkPolyData* simplified = decim->GetOutput();

SetTargetReduction(0.6):0~1之间。 0:不简化;1:减到0面片。

坑:简化过度会丢失细节,边角变圆;做预览可以用,不要把简化网格送回给CAD内核做计算。

7、vtkSmoothPolyDataFilter 网格平滑

对点坐标做平滑,消除三角化带来的锯齿毛刺。

#include <vtkSmoothPolyDataFilter.h>

vtkSmartPointer<vtkSmoothPolyDataFilter> smooth = vtkSmartPointer<vtkSmoothPolyDataFilter>::New();
smooth->SetInputData(polyData);
smooth->SetNumberOfIterations(20); //迭代次数,越大越平滑
smooth->Update();

坑:迭代次数太高,模型会收缩变形。CAD精确模型慎用平滑,只适合扫描模型。


高频Filter串联实战示例(剖切完整管线)

球体源 → 计算法向量 → 平面裁剪 → mapper渲染

sphereSource->Update();

vtkSmartPointer<vtkPolyDataNormals> normFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
normFilter->SetInputData(sphereSource->GetOutput());
normFilter->SetComputePointNormals(true);
normFilter->Update();

vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
plane->SetOrigin(0,0,0);
plane->SetNormal(1,0,0);

vtkSmartPointer<vtkClipPolyData> clip = vtkSmartPointer<vtkClipPolyData>::New();
clip->SetInputData(normFilter->GetOutput());
clip->SetClipFunction(plane.Get());
clip->Update();

mapper->SetInputData(clip->GetOutput());

管线链路: sphereSource → PolyDataNormals → ClipPolyData → mapper

本节核心小结(记住区分)

  1. vtkPolyDataNormals:计算法向量,解决模型发黑,CAD三角化后几乎必跑。
  2. vtkClipPolyData:平面裁剪,保留一半实体模型,生成切面三角面片。
  3. vtkCutter:求交,输出截面相交线,没有面片。
  4. vtkAppendPolyData:多个网格拼接;拼接完建议接CleanPolyData去重点。
  5. vtkQuadricDecimation:网格减面,优化渲染性能,预览用,不用于计算。
  6. vtkSmoothPolyDataFilter:平滑网格,CAD精确实体谨慎使用。

工业开发真实工作流回顾

OCCT读取STEP → 三角化得到PolyData → PolyDataNormals修复法向量 →(可选:裁剪/切片/合并/减面)→ VTK渲染、拾取。

所有这些Filter操作的,全部是离散三角网格,不是原始B‑Rep实体。Filter改完网格,无法反向回传给OCCT得到修改后的CAD实体。


新手高频坑汇总

  1. Clip/Cutter输入网格没有法向量,结果错乱。
  2. AppendPolyData合并后不做Clean,存在大量重复点,拾取、渲染异常。
  3. 过度简化网格,零件几何特征丢失。
  4. 把平滑Filter用在精确CAD零件,模型发生形变。

问题来了

1,2,3 OCCT不是可以做吗?为什么要放行到VTK这里才做?

核心问题:OCCT明明能干法向量、裁剪、切片,为什么还要跑到VTK做?

先一句话划清边界:

OCCT做B‑Rep精确几何;VTK做离散三角网格。二者操作的数据对象根本不一样。 OCCT可以做【CAD实体层面】的剖切、求交;VTK做【三角网格层面】的剖切、求交。 很多人混淆:把“渲染时的视觉剖切”和“修改原始CAD实体”当成同一件事。

我们逐个拆解前面3个Filter:vtkPolyDataNormals、vtkClipPolyData、vtkCutter。

1、vtkPolyDataNormals(计算法向量)

OCCT内部Face本身就有精确数学法向量,但是!这个法向量是属于BRep面的,不是属于VTK三角面片的。

OCCT三角化(tessellate)把NURBS曲面打散成一堆三角面片输出vtkPolyData时:

  • OCCT可以把面法向量拷给每一个三角;
  • 但三角化之后,多个Face拼接的接缝处、碎面片、破损网格,经常出现面片朝向混乱、正反颠倒。
  • OCCT输出的三角网格,不会自动给每个顶点插值点法向量(点法向量用来做平滑着色)。

两种方案对比:

  1. OCCT侧:拿每个Face的法向量,手动遍历每一个三角,赋值,自己做顶点法向量插值。代码量大,自己写很多逻辑。
  2. VTK vtkPolyDataNormals:输入一份PolyData,一行配置,自动统一环绕顺序、翻转面片、计算Cell法向量、插值Point点法向量,直接输出可渲染网格。

真实工业场景: OCCT输出三角网格 → 直接丢进vtkPolyDataNormals修复一遍,属于渲染流水线的预处理步骤。 它不是修改原始BRep实体,只是修复给GPU看的那一份网格。

关键点:OCCT的法向量依附于BRep的Face;VTK的法向量依附于三角面片/顶点。数据载体不一样,不能直接拿来就用。

2、vtkClipPolyData(网格裁剪,剖切视图)

OCCT当然可以做剖切:BRepAlgoAPI_Cut布尔差集,输入TopoDS_Shape,输出新的BRep实体。 但是这里有巨大现实代价:

方案A:OCCT做实体剖切(BRepAlgoAPI_Cut)

✅优点:输出是真正BRep实体,精度无损,可以保存stp,可以继续做布尔、倒角等CAD操作。 ❌缺点:

  1. 布尔运算非常慢,复杂装配体,交互式拖动剖切平面,每一帧都跑一次布尔,帧率直接崩盘,做不到实时拖拽剖切。
  2. 布尔运算容差、拓扑容错很麻烦,复杂模型很容易算失败、生成无效拓扑。
  3. 剖切完之后,你还得重新三角化一遍新BRep,再送给VTK渲染。每拖动一次剖切平面,完整链路:布尔运算 → 修复拓扑 → 三角化 → 送VTK。开销巨大。
方案B:VTK vtkClipPolyData(网格层面裁剪)

输入:已经三角化好的vtkPolyData,输出裁剪后的三角网格。 ✅优点:

  1. 速度极快,全部是三角面片求交,CPU多线程,拖动剖切平面每一帧实时算,适合交互视图。

  2. 完全不碰原始OCCT‑BRep实体!原始STEP实体完好无损,没有被修改。 ❌缺点:

    输出只是一份被切开的三角网格,不是BRep实体,不能回传给OCCT做CAD建模,不能导出stp,仅仅用于显示。

这就是工业软件里「视觉剖切」的标准实现: 用户拖动剖切滑块 → 只修改显示出来的三角网格,底层CAD实体纹丝不动;关掉剖切,原始模型直接恢复。 绝大多数三维软件的剖切视图,都是VTK网格裁剪,不是真的把实体切掉。

补充:OCCT也有Graphic3d_ClipPlane,是GPU层面的视图裁剪,显卡直接丢弃像素,不会生成切面三角面片,你看不到切开之后的内部截面,只能看到模型被切掉一半,没有切面填充色。而vtkClipPolyData会实实在在生成切面三角面片,可以给切面上色。

3、vtkCutter(求交,获取截面交线)

OCCT对应接口:BRepAlgoAPI_Section,BRep实体和平面求交,输出BRep的Wire交线(精确曲线)。

OCCT方案

输出精确NURBS交线Wire,精度高;但是复杂模型求交慢,得到Wire之后,如果你要在VTK渲染这条交线,还需要把Wire离散成线段,再转vtkPolyData。

VTK vtkCutter方案

直接对三角网格求交,输出vtkPolyData的线段。

  • 如果你只需要在界面上画一条截面线看效果,VTK一步到位,不用来回BRep转网格。
  • 缺点:交线是离散折线,不是解析曲线,精度受原始三角网格分辨率限制。
什么时候选谁
  1. 要拿到精确几何结果,后续要做CAD计算、导出:用OCCT的BRepAlgoAPI_Section。
  2. 仅仅视图显示截面轮廓,交互拖动平面实时预览:直接上vtkCutter。

一张表把区别讲透

操作 OCCT(BRep层面) VTK(PolyData网格层面)
法向量 属于Face曲面,无顶点平滑法向量,需要自己组装到三角网格 直接批量计算面片/顶点法向量,修复朝向,输出渲染就绪网格
剖切裁剪 BRepAlgoAPI_Cut,输出新BRep实体;慢,可保存STEP,可继续CAD编辑 vtkClipPolyData,输出三角网格;快,交互实时;仅用于显示,不能回写CAD
截面求交 BRepAlgoAPI_Section,输出精确Wire曲线;求交开销大 vtkCutter,输出离散线段;速度快,只做可视化预览

最重要的工程思想(你一定要记住)

OCCT负责“真实的数据(BRep)”;VTK负责“给屏幕看的那一份离散副本”。

工作流:

  1. stp读入OCCT,得到原始BRep(这个是本源,不能随便动)
  2. OCCT三角化,生成一份离散三角网格副本交给VTK
  3. 所有交互式剖切、裁剪、法向量修复,全部在这份网格副本上做,原始BRep完全不动
  4. 用户点保存STEP,保存的永远是OCCT那边的原始BRep,不是VTK处理过的网格。

如果你每一次拖动剖切平面,都去修改OCCT的BRep实体,那就是把“视觉显示”和“真实模型数据”混在一起,会灾难:性能爆炸,原始模型被破坏,撤销逻辑极其难写。

什么时候才真的去OCCT做剖切?

当用户点击【应用剖切、生成新零件】,需要把剖切之后的模型导出STEP,真正生成新实体,这时才调用OCCT布尔运算,生成新BRep。 单纯预览看剖面,全部交给VTK Filter。

坑点提醒

很多新手踩坑:

拿VTK Clip输出的PolyData,想回传给OCCT,转BRep,导出stp。 ❌行不通。VTK输出是离散三角网格,丢失NURBS曲面拓扑,不能反向恢复精确CAD实体。

VTK Filter产出物,只用于可视化、仿真后处理;真正CAD修改,交给OCCT。


一般不就是交由 OCC 去计算哪些指标然后组装目标 B-rep 吗,既然都到 VTK 了,也就是到渲染的这一步了,计算这些指标的目的何在呢

你这个疑问抓到了工业软件架构最核心的矛盾点,很多做OCCT+VTK开发的人很久都捋不清这件事。

先复述你的逻辑:

真正几何计算、布尔、剖切、求交、指标计算,本来就该OCCT对B‑Rep做,得到正确B‑Rep。 VTK只是渲染层,拿到三角网格只管画出来就完事。 那为什么渲染这一步还要在VTK里面再做一遍裁剪、求交、算法向量?这些计算的意义在哪?

答案分两层:一部分是真的为渲染本身服务;另一部分是做交互式视觉效果,不改动底层B‑Rep。

第一类:vtkPolyDataNormals,纯粹为渲染服务,不是业务计算

OCCT的BRep Face有数学法向量,但那是曲面的解析法向量,不是GPU渲染需要的顶点/面片法向量。

OCCT做tessellation三角化的时候:

  1. 它可以把每个三角面片的法向量填好(cell normal)。
  2. 但是它不会自动生成顶点法向量(point normal)。 而GPU要做平滑着色(Gouraud着色),必须每个顶点带法向量。 没有顶点法向量,模型就是一块块硬三角面片,看着全是棱角,视觉效果很差。

这件事不属于CAD业务指标计算,它就是显卡渲染的输入要求。 OCCT内核不关心“屏幕上好不好看”,OCCT只关心几何精度、拓扑;渲染需要的着色相关数据,OCCT不会帮你全部组装好。 所以到VTK这一层算法向量,不是算业务指标,是组装GPU的渲染输入数据。

你完全可以自己在OCCT三角化之后,遍历所有三角,自己写代码计算顶点法向量,填进vtkPolyData。只是VTK已经封装好这个逻辑,不用自己手写一大段。

第二类:Clip / Cutter,这里才是最容易混淆的地方

你的思路:

如果要剖切,就在OCC做布尔,生成新B‑rep,再三角化送VTK渲染。

这个逻辑在“最终生成零件”的时候完全正确。 但是在用户交互预览阶段,不能这么干。

举真实软件场景:用户拖动剖切滑块,实时拖动切面,来回拉动看内部。

  • 如果每拖动一像素,就调用OCCT布尔差集:
    1. 布尔运算开销巨大,复杂装配体毫秒级根本下不来;
    2. 布尔会修改BRep拓扑;
    3. 布尔会产生失败、报错、拓扑破损;
    4. 算完还要重新三角化。

结果就是:拖动滑块卡顿、掉帧,甚至直接算崩。

所以软件做了一层分离:

  • 底层数据源:原始BRep,纹丝不动,不做任何布尔修改。
  • 显示副本:一份三角网格,交给VTK,每一帧在网格层面做clip。

用户看到的剖切效果,只是屏幕上的视觉假象,底层CAD模型根本没有被切。 用户只是想看一眼内部,并不想真的把零件切掉保存。

👉 VTK做clip,不是产出业务上有效的BRep结果,只是产出一帧画面。 它不产生可以保存、可以导出stp、可以继续建模的几何,仅仅服务交互预览。

只有当用户点击【确认剖切,生成新零件】按钮,业务层才真正调用OCCT布尔,组装出新BRep。 拖动滑块预览阶段,完全不走OCCT布尔。

这里就是很多人概念混淆的根源
  • OCC计算 → 产出业务可用的BRep,可以保存、建模、导出。
  • VTK Filter计算 → 产出仅用于这一帧显示的临时网格,用完就扔,不回写业务数据。
那为什么不全部交给GPU做裁剪,连VTK Filter都不用?

比如OCCT自带Graphic3d_ClipPlane,是GPU直接丢弃像素。

  • GPU裁剪:模型直接被“切掉看不见”,不会生成切面三角面片。切开之后的内壁是空的,没有面,看不到切面颜色。
  • VTK ClipFilter:CPU算出新三角面片,切面有真实面片,可以上色、拾取切面、显示切面的网格。

要看到填充的切面,就必须在CPU生成那一批切面三角单元,这一步就落到VTK Filter上。

Cutter截面求交同理
  • 如果业务需要精确交线,用于后续建模、导出:OCCT BRepAlgoAPI_Section,输出Wire。

  • 如果只是鼠标拖动平面,界面上实时画一条截面线给用户看,不需要后续处理:直接VTK Cutter对三角网格求交,得到线段直接渲染。

    这个交线精度受三角网格分辨率限制,只用来预览,不作为业务输出。

把整个架构逻辑浓缩成几句话
  1. OCCT负责业务真值:所有需要保存、导出、参与建模的几何,一定在BRep层面完成。
  2. OCCT输出三角网格,只是生成一份用于显示的副本,副本和原始BRep是割裂的。
  3. VTK这边的Filter分两种用途:
    • ①渲染预处理:算顶点法向量这类,GPU硬件的硬性输入要求,和CAD业务计算无关。
    • ②交互式视觉预览:clip、cutter,只生成临时显示网格,不动底层BRep,不产出业务结果,追求交互速度。
  4. VTK的这些计算,不是用来算业务指标、组装BRep的;业务指标、BRep组装依旧全部交给OCC。VTK不碰业务真值。
一个非常重要的工程原则

永远不要把“视图层为了画画面做的计算结果”,当成业务数据。

这就是很多新手踩大坑: 拿VTK clip之后的三角网格,想反向转成BRep,当成剖切后的零件去保存stp。 ❌完全错误。这个只是画面副本,不是业务真值。

业务真值永远呆在OCCT的TopoDS_Shape里面,VTK只是它的可视化镜子。镜子里面做的各种变形、剖切,只是影像,不是本体被改动。

一个小比喻帮助理解
  • OCCT(BRep):真实雕塑本体。
  • OCCT三角化:给雕塑拍一张照片(离散网格副本)交给VTK。
  • VTK Filter:拿照片做PS,裁剪照片、调光影。 PS改照片,雕塑本体没有变化。如果你想要真正把雕塑切开,你得回头拿工具去切真实雕塑本体(OCCT布尔),而不是修改照片。

VTK的clip/cutter就相当于PS修照片,只是改图片;真正修改雕塑本体,回到OCCT。


posted @ 2026-08-24 16:11  Drunker•L  阅读(18)  评论(0)    收藏  举报