【译者:这个系列教程是以Kitware公司出版的《VTK User’s Guide -11th edition》一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934-23-8),由于时间关系,我们不能保证每周都能更新本书内容。但尽量做到一周更新一篇到两篇内容。敬请期待^_^。欢迎转载,另请转载时注明本文出处,谢谢合作。同时,由于译者水平有限,出错之处在所难免,欢迎指出订正!】
【本小节内容对应原书的第95页至第105页】
流线(Streamlines)
流线可以看做无重量粒子在向量场(如速度场)中的移动路径。流线可以表达向量场的结构。通常可以创建多个流线来探索向量场中的感兴趣特征。
如图5-4。流线可以通过数值积分来计算。因此只能近似的模拟真实的流线。
图5-4 被管道所包围的流线
创建流线需要指定起始点,方向(沿着或者反着流向)。以及其他的控制前进的参数。下面代码说明了如何创建一条流线。
该流线由一个管道表示,管道半径正比于速度模值的倒数。当流比较小时,管道会比较粗。反之亦然。Tcl代码摘自VTK/Exmaples/VisualizationAlgorithms/Tcl/OfficeTube.tcl。
vtkStructuredGridReader reader reader SetFileName"$VTK_DATA_ROOT/Data/office.binary.vtk" reader Update;#force a read to occur vtkRungeKutta4 integ vtkStreamTracer streamer streamer SetInputConnection [reader GetOutputPort] streamer SetStartPosition 0.1 2.1 0.5 streamer SetMaximumPropagation 500 streamerSetMaximumPropagationUnitToTimeUnit streamer SetInitialIntegrationStep 0.05 streamer SetInitialIntegrationStepUnitToCellLengthUnit streamer SetIntegrationDirectionToBoth streamer SetIntegrator integ vtkTubeFilter streamTube streamTube SetInputConnection [streamer GetOutputPort] streamTube SetInputArrayToProcess 1 0 0vtkDataObject::FIELD_ASSOCIATION_POINTS vectors streamTube SetRadius 0.02 streamTube SetNumberOfSides 12 streamTube SetVaryRadiusToVaryRadiusByVector vtkPolyDataMapper mapStreamTube mapStreamTube SetInputConnection[streamTube GetOutputPort] eval mapStreamTube SetScalarRange [[[[reader GetOutput] GetPointData]GetScalars] GetRange] vtkActor streamTubeActor streamTubeActor SetMapper mapStreamTube [streamTubeActor GetProperty] BackfaceCullingOn
在该例中我们设置起始点为(0.1,2.1, 0.5)。
也可以通过指定单元id。单元子id和参数坐标来指定起始坐标。MaximumPropagation变量用来控制流线的最大长度(度量单位由MaximumPropagationUnit变量指定)。如果需要更高的精度(代价是更多的计算时间),设置InitialIntegrationsetp变量为一个更小的值。(这里该参数指定为单元长度。也可以选择时间或者距离)另外。精度也可以通过选择一个vtkInitialValueProblemSolver的子类如vtkRungeKutta2或者vtkRungeKutta45(可以设置自适应的步长)来控制。默认情况下,流线追踪类利用vtkRungeKutta2来执行数值积分运算。积分方向可以由以下三个函数控制。
- SetIntegrationDirectionToForward()
- SetIntegrationDirectionToBackward()
- SetIntergrationDirectionToBoth()
由于线通常难以观察,因为我们采用一个管道Filter来表示。管道Filter设置为半径正比于速度模值的倒数,通过函数SetVaryRadiusToVaryRadiusByVector()开启该功能。也可以通过SetVaryRadiusToVaryRadiusByScalar()设置根据标量控制半径,或者取消半径可变(SetVaryRadiusToVaryRadiusOff())。注意需要通知管道filter利用哪个数组来控制半径。这里“vectors”数组通过SetInputArrayToProcess()函数通知管道filter。
有时候我们需要产生同步地产生许多流线。其中一个方法是利用SetSourceConnection()方法来指定一个vtkDataSet的实例。利用这个实例的点来追踪流线。下面是该应用的一个例子。代码摘自VTK/Examples/VisualizationAlgorithms/Tcl/OffcicesTubes.tcl。
vtkPointSource seeds seeds SetRadius 0.15 eval seeds SetCenter 0.1 2.1 0.5 seeds SetNumberOfPoints 6 vtkRungeKutta4 integ vtkStreamTracer streamer streamer SetInputConnection [reader GetOutputPort] streamer SetSourceConnection [seeds GetOutputPort] streamer SetMaximumPropagation 500 streamer SetMaximumPropagationUnitToTimeUnit streamer SetInitialIntegrationStep 0.05 streamer SetInitialIntegrationStepUnitToCellLengthUnit streamer SetIntegrationDirectionToBoth streamer SetIntegrator integ
注意该例中采用vtkPointSource对象来创建球状点云作为streamer的输入。
对于输入的每一个点都会计算一条流线。
流面(StreamSurfaces)
高级用户可能想利用VTK的流面计算功能。流面计算分为两步,首先用一个有序点集生成一个流线序列,然后再由vtkRuledSurfaceFilter来创建流面。vtkRuledSurfaceFilter假设每条流线是有序排列的,而且每条流线与左右相邻流线在指定距离内。因此输入点集保持有序十分重要。否则计算结果将会非常糟糕。
下面代码演示了如何创建一个流面(代码取自VTK/Examples/VisualizationAlgorithms/Tch/streamSurface.tcl。结果如图5-5)。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3d3X2RvbGluZ19uZXQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >
图5-5 流面
vtkLineSource rake rake SetPoint1 15 -5 32 rake SetPoint2 15 5 32 rake SetResolution 21 vtkPolyDataMapper rakeMapper rakeMapper SetInputConnection [rake GetOutputPort] vtkActor rakeActor rakeActor SetMapper rakeMapper vtkRungeKutta4 integ vtkStreamTracer sl sl SetInputConnection [pl3d GetOutputPort] sl SetSourceConnection [rake GetOutputPort] sl SetIntegrator integ sl SetMaximumPropagation 0.1 sl SetMaximumPropagationUnitToTimeUnit sl SetInitialIntegrationStep 0.1 sl SetInitialIntegrationStepUnitToCellLengthUnit sl SetIntegrationDirectionToBackward vtkRuledSurfaceFilter scalarSurface scalarSurface SetInputConnection [slGetOutputPort] scalarSurface SetOffset 0 scalarSurface SetOnRatio 2 scalarSurface PassLinesOn scalarSurface SetRuledModeToPointWalk scalarSurface SetDistanceFactor 30 vtkPolyDataMapper mapper mapper SetInputConnection [scalarSurface GetOutputPort] eval mapper SetScalarRange [[pl3d GetOutput] GetScalarRange] vtkActoractor actor SetMapper mapper
vtkRuledSurfaceFilter一个优点是当输入多个流线时可以关闭带功能,这样有助于理解曲面的结构。
切割(Cutting)
VTK中切割或者切面化数据集意味着在数据集中利用任意类型的隐函数来创建交叉区域。例如我们可以采用一个平面来创建一个平面切面对数据集进行切面显示。切面进行切割时对数据进行插值,然后采用任何一个标准可视化技术来显示。
切割结果通常是vtkPolyData类型(n维对象的切割结果是一个n-1维几何体。例如,切割一个四面体将产生一个三角形或者四边形)。
下面Tcl实例中演示了一个燃烧室被平面切割的结果。如图5-6。代码摘自VTK/Graphics/Tesing/Tcl/pro 。
vtkPlane plane eval plane SetOrigin [[pl3d GetOutput] GetCenter] plane SetNormal -0.287 0 0.9579 vtkCutter planeCut planeCut SetInputConnection [pl3d GetOutputPort] planeCut SetCutFunction plane vtkProbeFilter probe probe SetInputConnection [planeCut GetOutputPort] probe SetSourceConnection [pl3d GetOutputPort] vtkDataSetMappercutMapper cutMapper SetInputConnection [probe GetOutputPort] eval cutMapper SetScalarRange [[[[pl3d GetOutput] GetPointData] GetScalars] GetRange] vtkActor cutActor cutActor SetMapper cutMapper
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3d3X2RvbGluZ19uZXQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >
图5-6 燃烧室切割效果图
vtkCutter需要指定完成切割的隐函数。另外,你可能希望指定一个或者多个切割值,可以通过SetValue()或者GenerateValues()函数实现。这些数值指定了执行切割的隐式函数值。(默认情况下切割值为0 ,即切面精确地位于隐函数曲面,小于或者大于0值的曲面则位于该隐曲面的下面和上面。切割值可以看做是到隐曲面的“距离”。)
合并数据(MergingData)
到目前为止我们已经了解了简单的线性可视化管线。
然后,管线还可以存在分支,合并甚至是循环情况。
接下来我们介绍两个Filter,它们可以利用其他的数据集来构建新的数据集。现在从vtkMergeFilter开始。
vtkMergeFilter将多个数据集中的数据片段合并为一个新的数据集。例如,可以将一个数据集的结构(拓扑和几何),第二个数据集的标量数据,第三个数据集的向量数据合并为一个数据集。这里是该应用的一个实例。
(代码摘自VTK/Examples/VisualizationAlgorithms/Tcl/imageWarp.tcl)。(主要关注vtkMergeFilter,其他的不熟悉的可以先忽略。
在第106页我们将更加详细的进行描述。
)
vtkBMPReader reader reader SetFileName $VTK_DATA_ROOT/Data/masonry.bmp vtkImageLuminance luminance luminance SetInputConnection [reader GetOutputPort] vtkImageDataGeometryFilter geometry geometry SetInputConnection [luminance GetOutputPort] vtkWarpScalarwarp warp SetInputConnection [geometry GetOutputPort] warp SetScaleFactor -0.1 vtkMergeFilter merge merge SetGeometryConnection [warp GetOutputPort] merge SetScalarsConnection [reader GetOutputPort] vtkDataSetMapper mapper mapper SetInputConnection [merge GetOutputPort] mapper SetScalarRange 0 255 mapper ImmediateModeRenderingOff vtkActor actor actor SetMapper mapper<span style="font-size: 14px; line-height: 1.846153846; font-family: Arial; background-color: rgb(255, 255, 255);"> </span>
这里所做的是将vtkWarpScalar(输出是vtkPolyData类型)的输出与vtkBMPReader标量数据合并到一起。
管线先分后合。因为几何结构需要利用标量数据来单独处理。
当合并数据时。数组中的元组数必须与点的个数一致。单元数据也是一样。
追加数据(AppendingData)
类似vtkMergeFilter,vtkAppendFilter以及vtkAppendPolyData通过追加数据集来产生新数据集。追加Filter接收一系列输入,这些输入的类型必须一致。
在追加操作中。只有那些共同的数据数据才会合并在一块。
下一节中演示了一个比较好的实例。
探测(Probing)
探测是利用其它数据集对数据集进行采样的过程。
在VTK中,可以利用任意的数据集来作为探测几何,其点属性则由其它数据集映射而来。例如下例中创建了三个平面作为探测几何来对一个结构网格数据集进行采样。然后这些平面通过vtkContourFilter来计算等值线。代码摘自VTK/Examples/VisualizationAlgorithms/Tcl/probeComb.tcl。
vtkPLOT3DReader pl3d pl3d SetXYZFileName "$VTK_DATA_ROOT/Data/combxyz.bin" pl3d SetQFileName "$VTK_DATA_ROOT/Data/combq.bin" pl3d SetScalarFunctionNumber 100 pl3d SetVectorFunctionNumber 202 pl3d Update vtkPlaneSource plane plane SetResolution 50 50 vtkTransform transP1 transP1 Translate 3.7 0.0 28.37 transP1 Scale 5 5 5 transP1 RotateY 90 vtkTransformPolyDataFilter tpd1 tpd1 SetInputConnection [plane GetOutputPort] tpd1 SetTransform transP1 vtkOutlineFilter outTpd1 outTpd1 SetInputConnection [tpd1 GetOutputPort] vtkPolyDataMapper mapTpd1 mapTpd1 SetInputConnection [outTpd1 GetOutputPort] vtkActort pd1Actor tpd1Actor SetMapper mapTpd1 [tpd1Actor GetProperty] SetColor 0 0 0 vtkTransform transP2 transP2 Translate 9.2 0.0 31.20 transP2 Scale 5 5 5 transP2 RotateY 90 vtkTransformPolyDataFilter tpd2 tpd2 SetInputConnection [plane GetOutputPort] tpd2 SetTransform transP2 vtkOutlineFilter outTpd2 outTpd2 SetInputConnection [tpd2 GetOutputPort] vtkPolyDataMapper mapTpd2 mapTpd2 SetInputConnection [outTpd2 GetOutputPort] vtkActor tpd2Actor tpd2Actor SetMapper mapTpd2 [tpd2Actor GetProperty] SetColor 0 0 0 vtkTransform transP3 transP3 Translate 13.27 0.0 33.30 transP3 Scale 5 5 5 transP3 RotateY 90 vtkTransformPolyDataFilter tpd3 tpd3 SetInputConnection [plane GetOutputPort] tpd3 SetTransform transP3 vtkOutlineFilter outTpd3 outTpd3 SetInputConnection [tpd3 GetOutputPort] vtkPolyDataMapper mapTpd3 mapTpd3 SetInputConnection [outTpd3 GetOutputPort] vtkActort pd3Actor tpd3Actor SetMapper mapTpd3 [tpd3Actor GetProperty] SetColor 0 0 0 vtkAppendPolyData appendF appendF AddInputConnection [tpd1 GetOutputPort] appendF AddInputConnection [tpd2 GetOutputPort] appendF AddInputConnection [tpd3 GetOutputPort] vtkProbeFilter probe probe SetInputConnection [appendF GetOutputPort] probe SetSourceConnection [pl3d GetOutputPort] vtkContourFilter contour contour SetInputConnection [probeGetOutputPort] evalcontour GenerateValues 50 [[pl3d GetOutput] GetScalarRange] vtkPolyDataMapper contourMapper contourMapper SetInputConnection [contour GetOutputPort] evalcontourMapper SetScalarRange [[pl3d GetOutput] GetScalarRange] vtkActor planeActor planeActor SetMapper contourMapper
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3d3X2RvbGluZ19uZXQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >
图5-7 数据探测
注意通过SetInputConnection()方法来设置探测器,而待探测数据集是通过SetSourceConnection()函数设置。
探测的另外一个应用是重采样数据。
例如。如果你有一个无结构网格数据,而你想通过专门可视化vtkImageData的工具来显示(例如体绘制。139页)。那么可以采用vtkProbeFilter对无结构网格数据采用为一个体数据,然后再可视化。
同样也可以采用直线来探测数据,并将结果绘制成X-Y曲线。
最后值得注意的是,切割和探测都能得到类似的结果。除了在分辨率上有所差别外。类似于98页的切割实例。vtkProbeFilter可以利用vtkPlaneSource来产生一个平面,而该平面的属性数据来自于结构网格数据。然而。切割产生的曲面分辨率要依赖于输入数据。而探测产生的曲面分辨率则独立于输入数据。因此在探测数据时,需要特别注意低采用或者过采样。
低采样会导致显示错误。过采样则会占用过多的计算时间。
用其他标量着色等值面
计算等值面并用另一个标量进行着色是一个常见的可视化操作。可能你会想到用探测器,但是如果你等值面如果包含你想用来着色的数据时。会有一个更加有效的方法。因为vtkContourFilter(实际用来产生等值面)在计算过程中会对所有数据进行插值。插值后的数据在映射过程中用来进行着色。下面例子取自VTK/Examples/VisualizationAlgorithms/Tcl/ColorIsosurface.tcl。
vtkPLOT3DReader pl3d pl3d SetXYZFileName "$VTK_DATA_ROOT/Data/combxyz.bin" pl3d SetQFileName "$VTK_DATA_ROOT/Data/combq.bin" pl3d SetScalarFunctionNumber 100 pl3d SetVectorFunctionNumber 202 pl3d AddFunction 153 pl3d Update vtkContourFilter iso iso SetInputConnection [pl3d GetOutputPort] iso SetValue 0.24 vtkPolyDataNormals normals normals SetInputConnection [iso GetOutputPort] normals SetFeatureAngle 45 vtkPolyDataMapper isoMapper isoMapper SetInputConnection [normals GetOutputPort] isoMapper ScalarVisibilityOn isoMapper SetScalarRange 0 1500 isoMapper SetScalarModeToUsePointFieldData isoMapper ColorByArrayComponent "VelocityMagnitude" 0 vtkLODActor isoActor isoActor SetMapper isoMapper isoActor SetNumberOfCloudPoints 1000
图5-8 用其他标题着色等值面
首先通过vtkPLOT3DReader读取数据集。
这里我们添加一个要读的函数(函数号153),函数名字是“Velocity Magnitude”。
计算等值面时。也会对所有输入数据包括速度场数据进行插值。然后我们调用SetScalarModeToUsePointFieldData()函数来利用速度模值来对等值面着色,ColorByArrayComponent()方法用来指定着色所用的数据数组。
提取单元子集
可视化数据的数据量一般都非常大,处理这样的数据往往要耗费大量的时间和内存。因此,提取部分数据功能显示非常重要。多数情况下。只有部分数据包含有意义的信息,或者在不影响精度的条件下对数据进行消减。
VTK中提供了许多工具来提取部分数据或者对数据降采样。我们已经了解到vtkProbeFilter可以用来进行降采样(见100页“Probing”)。其他工具包含降采样类,还有一些工具能够实现从空间区域中提取单元(降采样工具针对于特定数据,详见105页“降采样图像数据”,和113页“降采样规则网格数据”)。本节中我们主要讲述怎样在空间区域中提取数据片段。
vtkExtractGeometryl类提取数据集中位于隐函数曲面vtkImplicitFucntion内部或者外部的所有单元(注意,隐函数可以是多个隐式函数的二值组合)。下面代码创建了两个椭球的二值组合用来提取区域。vtkShrinkFilter用来对单元进行收缩以便能观察被提取的数据。
(代码取自VTK/Examples/VisualizationAlgorithms/Tcl/ExtractionGeometry.tcl.)
vtkQuadric quadric quadric SetCoefficients .5 1 .2 0 .1 0 0 .20 0 vtkSampleFunction sample sample SetSampleDimensions 50 50 50 sample SetImplicitFunction quadric sample ComputeNormalsOff vtkTransform trans trans Scale 1 .5 .333 vtkSphere sphere sphere SetRadius 0.25 sphere SetTransform trans vtkTransform trans2 trans2 Scale .25 .5 1.0 vtkSphere sphere2 sphere2 SetRadius 0.25 sphere2 SetTransform trans2 vtkImplicitBoolean union union AddFunction sphere union AddFunction sphere2 union SetOperationType 0;#union vtkExtractGeometryextract extract SetInputConnection [sampleGetOutputPort] extract SetImplicitFunction union vtkShrinkFilter shrink shrink SetInputConnection [extract GetOutputPort] shrink SetShrinkFactor 0.5 vtkDataSetMapper dataMapper dataMapper SetInputConnection [shrink GetOutputPort] vtkActor dataActor dataActor SetMapper dataMapper
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3d3X2RvbGluZ19uZXQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" >
图5-9 提取单元的子集
vtkExtractGeometry的输出通常是vtkUnstructuredGrid类型。因为在提取过程中,数据集的拓扑结构往往被破坏,因此必须采用最普遍的数据格式来表示输出。
注意,隐式函数可以通过分配一个vtkTransform来进行变换。
如果指定了变换,vtkTransform就被用来改变隐式函数的取值。
你可能希望对该功能进行测试。
以多边形数据类型为输出提取单元
大部分数据类型不能直接被图形硬件或者图形库渲染。渲染系统中只有多边形数据(vtkPolyData)被广泛的支持。规则数据集,特别是图像以及体数据也可以被图形系统支持。
而其他的数据则需要特别的处理才能被渲染。
在VTK中,渲染非多边形数据的一个方法是将其转换为多边形数据。vtkGeometryFilter可实现该功能。
vtkGeometryFilter接收任意vtkDataSet类型数据并输出vtkPolyData数据。其执行转换时,遵循如下规则。
所有二维及以下拓扑单元(如多边形。直线,顶点)直接传递至输出。位于数据集边界的三维单元面会被传递至输出结果中。(如果一个面只属于一个单元,那么这个面位于边界。
)
vtkGeometryFilter常被用来进行数据格式转换。
下面代码中利用vtkGeometryFilter来将二维不规则网格转换为多边形数据,这些多边形数据接下来会作为其他接收vtkPolyData的Filter所接收。
代码取自VTK/Examples/DataManipulation/Tcl/pointToCellData.tcl。
这里vtkConnectivityFilter提取vtkUnstructedGrid数据然后通过vtkGeometryFilter将其转换为多边形数据。
vtkConnectivityFilter connect2 connect2 SetInputConnection [thresh GetOutputPort] vtkGeometryFilter parison parison SetInputConnection [connect2 GetOutputPort] vtkPolyDataNormals normals2 normals2 SetInputConnection [parison GetOutputPort] normals2 SetFeatureAngle 60 vtkLookupTable lut lut SetHueRange 0.0 0.66667 vtkPolyDataMapper parisonMapper parisonMapper SetInputConnection [normals2 GetOutputPort] parisonMapper SetLookupTable lut parisonMapper SetScalarRange 0.12 1.0 vtkActor parisonActor parisonActor SetMapper parisonMapper
实际上vtkDataSetMapper内部使用vtkGeometryFilter来将任意数据类型转换为多边形数据。(该Filter可以直接将输入的vtkPolyData传递到输出。)
另外vtkGeometryFilter提供了一系列函数来根据点id集合。单元ids集合或者判断是否位于一个特定的矩形空间区域中来提取数据单元。利用点或者面id集合提取数据片段时,使用的函数有PointClippingOn(),SetPointMinimum(),SetPointMaximum()和CellClippingOn(),SetCellMinimum(),SetCellMaximum()。
最小值和最大值指定了提取的id 的范围。
同样还可以指定一个空间矩形区域来限制提取的范围。
用ExtentClippingOn()和SetExtent()来开始空间切割和指定范围。
Extent包含了六个参数来定义一个包围-
,