8.几何处理Filter实战
第七部分:几何处理Filter实战(工业软件高频)
前面已经懂Filter通用机制:输入数据集,输出新数据集,Update触发计算。 这里全部是做三维CAD后处理、模型预览、仿真前处理最常用的一批Filter,讲清楚用途、典型场景、坑点,附带最简代码片段。
分为4大类:法向量处理、裁剪切片、网格合并提取、网格简化平滑。
前置提醒: 大部分Filter接收
vtkPolyData;输入输出都是PolyData;SetInputData()接入上游,Update()执行; Filter输出是全新数据,原始输入不会被修改。
1、vtkPolyDataNormals 计算法向量(极其高频)
作用
计算面片法向量、点法向量,存入PolyData的PointData / CellData数组。
为什么工业软件必须用
-
没有法向量,光照渲染异常:模型发黑、半边黑半边亮、反光错乱。
-
很多其它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();
关键点
- 裁剪会生成新的三角面片,就是切开之后的切面;点、cell数量会变化。
SetInsideOut()控制保留哪一半,做剖切视图经常切换这个开关。- 输入需要有法向量,否则裁剪效果异常。
注意: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();
对比记忆
- Clip:切割实体,输出三角面片(半个模型)
- 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
本节核心小结(记住区分)
- vtkPolyDataNormals:计算法向量,解决模型发黑,CAD三角化后几乎必跑。
- vtkClipPolyData:平面裁剪,保留一半实体模型,生成切面三角面片。
- vtkCutter:求交,输出截面相交线,没有面片。
- vtkAppendPolyData:多个网格拼接;拼接完建议接CleanPolyData去重点。
- vtkQuadricDecimation:网格减面,优化渲染性能,预览用,不用于计算。
- vtkSmoothPolyDataFilter:平滑网格,CAD精确实体谨慎使用。
工业开发真实工作流回顾
OCCT读取STEP → 三角化得到PolyData → PolyDataNormals修复法向量 →(可选:裁剪/切片/合并/减面)→ VTK渲染、拾取。
所有这些Filter操作的,全部是离散三角网格,不是原始B‑Rep实体。Filter改完网格,无法反向回传给OCCT得到修改后的CAD实体。
新手高频坑汇总
- Clip/Cutter输入网格没有法向量,结果错乱。
- AppendPolyData合并后不做Clean,存在大量重复点,拾取、渲染异常。
- 过度简化网格,零件几何特征丢失。
- 把平滑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输出的三角网格,不会自动给每个顶点插值点法向量(点法向量用来做平滑着色)。
两种方案对比:
- OCCT侧:拿每个Face的法向量,手动遍历每一个三角,赋值,自己做顶点法向量插值。代码量大,自己写很多逻辑。
- 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操作。 ❌缺点:
- 布尔运算非常慢,复杂装配体,交互式拖动剖切平面,每一帧都跑一次布尔,帧率直接崩盘,做不到实时拖拽剖切。
- 布尔运算容差、拓扑容错很麻烦,复杂模型很容易算失败、生成无效拓扑。
- 剖切完之后,你还得重新三角化一遍新BRep,再送给VTK渲染。每拖动一次剖切平面,完整链路:布尔运算 → 修复拓扑 → 三角化 → 送VTK。开销巨大。
方案B:VTK vtkClipPolyData(网格层面裁剪)
输入:已经三角化好的vtkPolyData,输出裁剪后的三角网格。 ✅优点:
-
速度极快,全部是三角面片求交,CPU多线程,拖动剖切平面每一帧实时算,适合交互视图。
-
完全不碰原始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转网格。
- 缺点:交线是离散折线,不是解析曲线,精度受原始三角网格分辨率限制。
什么时候选谁
- 要拿到精确几何结果,后续要做CAD计算、导出:用OCCT的
BRepAlgoAPI_Section。 - 仅仅视图显示截面轮廓,交互拖动平面实时预览:直接上vtkCutter。
一张表把区别讲透
| 操作 | OCCT(BRep层面) | VTK(PolyData网格层面) |
|---|---|---|
| 法向量 | 属于Face曲面,无顶点平滑法向量,需要自己组装到三角网格 | 直接批量计算面片/顶点法向量,修复朝向,输出渲染就绪网格 |
| 剖切裁剪 | BRepAlgoAPI_Cut,输出新BRep实体;慢,可保存STEP,可继续CAD编辑 | vtkClipPolyData,输出三角网格;快,交互实时;仅用于显示,不能回写CAD |
| 截面求交 | BRepAlgoAPI_Section,输出精确Wire曲线;求交开销大 | vtkCutter,输出离散线段;速度快,只做可视化预览 |
最重要的工程思想(你一定要记住)
OCCT负责“真实的数据(BRep)”;VTK负责“给屏幕看的那一份离散副本”。
工作流:
- stp读入OCCT,得到原始BRep(这个是本源,不能随便动)
- OCCT三角化,生成一份离散三角网格副本交给VTK
- 所有交互式剖切、裁剪、法向量修复,全部在这份网格副本上做,原始BRep完全不动
- 用户点保存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三角化的时候:
- 它可以把每个三角面片的法向量填好(cell normal)。
- 但是它不会自动生成顶点法向量(point normal)。 而GPU要做平滑着色(Gouraud着色),必须每个顶点带法向量。 没有顶点法向量,模型就是一块块硬三角面片,看着全是棱角,视觉效果很差。
这件事不属于CAD业务指标计算,它就是显卡渲染的输入要求。 OCCT内核不关心“屏幕上好不好看”,OCCT只关心几何精度、拓扑;渲染需要的着色相关数据,OCCT不会帮你全部组装好。 所以到VTK这一层算法向量,不是算业务指标,是组装GPU的渲染输入数据。
你完全可以自己在OCCT三角化之后,遍历所有三角,自己写代码计算顶点法向量,填进vtkPolyData。只是VTK已经封装好这个逻辑,不用自己手写一大段。
第二类:Clip / Cutter,这里才是最容易混淆的地方
你的思路:
如果要剖切,就在OCC做布尔,生成新B‑rep,再三角化送VTK渲染。
这个逻辑在“最终生成零件”的时候完全正确。 但是在用户交互预览阶段,不能这么干。
举真实软件场景:用户拖动剖切滑块,实时拖动切面,来回拉动看内部。
- 如果每拖动一像素,就调用OCCT布尔差集:
- 布尔运算开销巨大,复杂装配体毫秒级根本下不来;
- 布尔会修改BRep拓扑;
- 布尔会产生失败、报错、拓扑破损;
- 算完还要重新三角化。
结果就是:拖动滑块卡顿、掉帧,甚至直接算崩。
所以软件做了一层分离:
- 底层数据源:原始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对三角网格求交,得到线段直接渲染。
这个交线精度受三角网格分辨率限制,只用来预览,不作为业务输出。
把整个架构逻辑浓缩成几句话
- OCCT负责业务真值:所有需要保存、导出、参与建模的几何,一定在BRep层面完成。
- OCCT输出三角网格,只是生成一份用于显示的副本,副本和原始BRep是割裂的。
- VTK这边的Filter分两种用途:
- ①渲染预处理:算顶点法向量这类,GPU硬件的硬性输入要求,和CAD业务计算无关。
- ②交互式视觉预览:clip、cutter,只生成临时显示网格,不动底层BRep,不产出业务结果,追求交互速度。
- 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。

浙公网安备 33010602011771号