VTK知识学习(41)-基本的图形操作(二)
1、包围盒
1)概述
包围盒是指能够包围模型的最小立方体,常常用于模型的碰撞检测中。vtkPolyData 中定义了函数 GetBounds()来获取包围盒的参数,即三个坐标轴方向上的最大、最小值。仅仅获取这些数据并不直观,有时候还需显示包围盒。vtkOutlineFilter 则提供一个方便的方法来生成包围盒,其输入为一个 vtkPolyData模型数据,输出为 vtkPolyData 数据,只需要将其作为vtkPolyDataMapper 的输入,建立可视化管线即可显示。
2)代码
private void TestBoundingBox()
{
//创建一个以原点为中心的多边形球 大小通过指定的半径来确定
// 分辨率 指在纬度和经度方向上的多边形离散化程序,即球体表面被划分的精细程序。 其值决定了在完整球体上纬度和经度线的数量。
// 通过指定最大Phi和theta角度,可以创建球体的部分区域,而不是整体球体。
vtkSphereSource sphereSource = vtkSphereSource.New();
sphereSource.SetCenter(0, 0, 0);
sphereSource.SetRadius(5);
sphereSource.Update();
vtkPolyData sphere = sphereSource.GetOutput();
vtkPolyDataMapper mapper = vtkPolyDataMapper.New();
mapper.SetInputData(sphere);
vtkOutlineFilter outLine = vtkOutlineFilter.New();
outLine.SetInputData(sphere);
outLine.Update();
vtkPolyDataMapper outlineMapper = vtkPolyDataMapper.New();
outlineMapper.SetInputData(outLine.GetOutput());
vtkActor actor = vtkActor.New();
actor.SetMapper(mapper);
vtkActor actor2 = vtkActor.New();
actor2.SetMapper(outlineMapper);
actor2.GetProperty().SetColor(1, 0, 0);
vtkRenderer renderer = vtkRenderer.New();
renderer.AddActor(actor);
renderer.AddActor(actor2);
renderer.SetBackground(1, 1, 1);
vtkRenderWindow renderWindow = renderWindowControl.RenderWindow;
renderWindow.AddRenderer(renderer);
renderWindow.Render();
}
3)效果

2、法向量计算
1)概述
三维平面的法向量是指垂直于该平面的三维向量。曲面在某点P处的法向量为垂直于该点切平面的向量。对于一个网格模型,其每一个点和单元都可以计算一个法向量,在三维计算机图形学中法向量一个重要的应用是光照和阴影计算。对于网格模型,模型是由一定数量的面片(单元)来逼近的,面片越多,则模型越精细:反之,则越粗糙。在计算网格模型的法向量时,单元法向量计算比较简单,可以通过组成每个单元的任意两条边的又乘向量并归-化来表示。而对于点的法向量,则是由所有使用该点的单元法向量的平均值来表示。
如图所示的立方体,对于平面1234的法向量N1可以由其任意两条非平行的边的叉乘来表示;同样可以计算得到面 1465 的法向量 N2 以及面 3764 的法向量 N3。而点2的法向量由于被面 1234、面 2762 和面 1265 公用,因此其法向量为三个面的法向量的平均向量。

VTK 中计算法向量的 Filter 为 vtkPolyDataNormals。该类针对单元为三角形或者多边形类型的 vtkPolyData 数据进行计算。由于法向量分为点法向量和单元法向量,可以通过函数SetComputeCell Normals()和 SetComputePointNormals()来设置需要计算的法向量类型。默认情况下计算点法向量,关闭单元法向量计算。
2)代码
private void TestDataNormal()
{
// vtkStructuredPointsReader reader = vtkStructuredPointsReader.New();
// vtkPLYReader reader = vtkPLYReader.New();
vtkPolyDataReader reader = vtkPolyDataReader.New();
reader.SetFileName("F:\\code\\VTK\\TestActiViz\\data\\fran_cut.vtk");
reader.Update();
//计算法向量
vtkPolyDataNormals normFilter = vtkPolyDataNormals.New();
normFilter.SetInputData(reader.GetOutput());
normFilter.SetComputePointNormals(1); //计算点法向量
normFilter.SetComputeCellNormals(0); //不计算单元法向量
normFilter.SetAutoOrientNormals(1); //自动调用法线方向
normFilter.SetSplitting(0); //关闭锐边缘处理
normFilter.Update();
//采样部分数据,仅保留输入数据中的点数据及其属性,并支持点数据的采样。
vtkMaskPoints mask = vtkMaskPoints.New();
mask.SetInputData(normFilter.GetOutput());
mask.SetMaximumNumberOfPoints(100); //采样100个点
mask.RandomModeOn(); //开启随机采样
// mask.Update();
//创建一个箭头源作为 glyph 的形状
vtkArrowSource arrow = vtkArrowSource.New();
//arrow.SetTipLength(0.1);
//arrow.SetTipRadius(0.05);
//创建一个 Glyph3D 映射器,它将glyph 放置在点源生成的点上
vtkGlyph3D plyph = vtkGlyph3D.New();
plyph.SetInputConnection(mask.GetOutputPort());
plyph.SetSourceConnection(arrow.GetOutputPort()); //实现在输入数据的每个点处会显示一个Glyph图形
// plyph.ScalingOff(); // 关闭自动缩放,以便使用默认大小
plyph.SetVectorModeToUseNormal(); //指定要使用法风和日丽数据来控制Glyph图形的方向
plyph.SetScaleFactor(0.01); //控制图形的大小
plyph.Update(); // 确保映射器被更新
ShowImage(reader.GetOutput(), normFilter.GetOutput(), plyph.GetOutput());
}
3)效果

4)说明
示例首先读取了一个模型文件并作为 vtkPolyDataNormals 对象的输入,设置需要计算的法向量类型,这里计算点法向量和单元法向量。调用Update()后即可生成一个 vtkPolyData数据,生成的法向量数据存储在其内部的 vtkPointData和 tkCellData中。可以通过如下函数来获取生成的法向量数据:
c++版本:
vtkFloatArray::SafeDownCast(normFilter->GetOutput()->GetPointData(->GetNormals();
vtkFloatArray::SafeDownCast(normFilter->GetOutput()->GetCellData()->GetNormals()
c#版本:
vtkDataArray dd= normFilter.GetOutput().GetPointData().GetNormals();
vtkDataArray dd2 = normFilter.GetOutput().GetCellData().GetNormals();
从图中可以看出,加入法向量后,模型显示得更加平滑,而未计算法向量的模型看起来比较粗糙。
注意:
在计算法向量时需要注意法向量的方向。因为对于同一个平面来讲,可以有两个方向完全相反的法向量。一般是根据单元的点顺序,采用右手法则来定义一个平面的法向量方向,图中面1234的法向量为垂直平面向外,因此在计算一个型的法向量时,这个方向会与单元的点顺序相关。必须保持单元的点顺序一致,才能得到合理的法向量。SetConsistency()可以设置自动调整模型的单元点顺序。SetAutoOrientNormals0)可以设置自动调整法线方向,而不需要用户来通过 SetFlipNormalsO)实现,因为许多情况下并不确定是否需要进行反向。
另外,vtkPolyDataNormals默认开启对锐边缘(SharpEdge)的处理。如果检测到存在锐边缘,则会将其分裂,因此模型的数据可能会发生变化。可以通过vkPolyDataNormals::SetSplitting()函数关闭该功能。
3、符号化Glpyhing
1)概述
模型的法向量数据是向量数据,因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通过符号化(Glyphing)技术将法向量图形化显示。Glyphing是一种基于图形的可视化技术,这些图像可以是简单的基本图形,如具有方向的锥体,也可以是更加复杂的图像。VTK 中使用vtkGlyph3D类可以实现该功能,并且可以支持 Glyph 图形的缩放、着色、设置空间姿态等。使用该类时,需要接受两个输入:一个是需要显示的几何数据点集合:另一个是 Glyph 图形数据,为 vtkPolyData数据。
2)代码
//采样部分数据,仅保留输入数据中的点数据及其属性,并支持点数据的采样。
vtkMaskPoints mask = vtkMaskPoints.New();
mask.SetInputData(normFilter.GetOutput());
mask.SetMaximumNumberOfPoints(100); //采样100个点
mask.RandomModeOn(); //开启随机采样
// mask.Update();
//创建一个箭头源作为 glyph 的形状
vtkArrowSource arrow = vtkArrowSource.New();
//arrow.SetTipLength(0.1);
//arrow.SetTipRadius(0.05);
//创建一个 Glyph3D 映射器,它将glyph 放置在点源生成的点上
vtkGlyph3D plyph = vtkGlyph3D.New();
plyph.SetInputConnection(mask.GetOutputPort());
plyph.SetSourceConnection(arrow.GetOutputPort()); //实现在输入数据的每个点处会显示一个Glyph图形
// plyph.ScalingOff(); // 关闭自动缩放,以便使用默认大小
plyph.SetVectorModeToUseNormal(); //指定要使用法风和日丽数据来控制Glyph图形的方向
plyph.SetScaleFactor(0.01); //控制图形的大小
plyph.Update(); // 确保映射器被更新
3)说明
由于读入的模型数据比较大,点比较多,因此使用vtkMaskPoints 类采样部分数据,该类仅保留输入数据中的点数据及其属性,并支持点数据的采样。为了减小计算量,随机采样了300个点做 Glyphing 显示。将其输出作为vtkGlyph3D的输入数据,而 SetSource()设置一个 vtkArrowSource 数据作为源数据,这样的效果是在输入数据的每个点处会显示一个 Glyph 图形,如本例中的箭头模型。vkGlyph3D::SetVectorModeToUseNormal()指定要使用法向量数据来控制Glyph图形的方向。vtkGlyph3D::SetScaleFactor()则控制 Glyph 图形的大小。定义完毕,为 Glyphing 结果定义相应的 vtkPolyDataMapper 和vtkActor 对象,然后同原始数据一起输入 vtkRenderer 进行显示,即可得到如图 所示的效果带有箭头的。
4、曲率计算
1)概述
曲率是曲面弯曲程度的一种度量,是几何体的一种重要的局部特征。如图6-11所示,要计算曲面上给定点M的曲率,考虑经过M的法线的一个平面与曲面相交,得到一条二维曲线称之为曲面在 M点的一条法截线,如图6-11所示中的C法;经过 M点法向量的曲面可以任意旋转,即可得到任意多条法截线,每条法截线会对应一个曲率,取具有最大曲率和最小曲率的两条法截线为主法截线,如图6-11所示中的G和C所示,其对应的曲率为ん和k,称为主曲率;高斯曲率等于主曲率的乘积,即んXk;平均曲率等于主曲率ん和ん的平均值,即(k+k)/2。以上只是曲率的直观几何解释,并没有给出具体的计算公式。

VTK中的 vtkCurvatures 类实现了四种计算网格模型点曲率的计算方法。该类接受一个vtkPolyData数据,将计算得到的曲率数据作为网格模型的点的属性数据存入返回的vtkPolyData 中。
2)代码
private void TestDataCurvature()
{
vtkPolyDataReader reader = vtkPolyDataReader.New();
reader.SetFileName("F:\\code\\VTK\\TestActiViz\\data\\fran_cut.vtk");
reader.Update();
//曲率计算
vtkCurvatures curvaturesFilter = vtkCurvatures.New();
curvaturesFilter.SetInputConnection(reader.GetOutputPort());
curvaturesFilter.SetCurvatureTypeToMaximum(); //计算最大主曲率
curvaturesFilter.Update();
double[] scalarRange = curvaturesFilter.GetOutput().GetScalarRange();
vtkLookupTable lut = vtkLookupTable.New();
lut.SetHueRange(0, 0.6);
lut.SetAlphaRange(1, 1);
lut.SetValueRange(1, 1);
lut.SetSaturationRange(1, 1);
lut.SetNumberOfTableValues(256);
lut.SetRange(scalarRange[0], scalarRange[1]);
lut.Build();
vtkPolyDataMapper mapper = vtkPolyDataMapper.New();
mapper.SetInputData(curvaturesFilter.GetOutput());
mapper.SetLookupTable(lut);
mapper.SetScalarRange(scalarRange[0], scalarRange[1]);
vtkActor actor = vtkActor.New();
actor.SetMapper(mapper);
//将一个颜色映射表转换为一个Actor对象
vtkScalarBarActor scalarBar = vtkScalarBarActor.New();
scalarBar.SetLookupTable(mapper.GetLookupTable());
scalarBar.SetTitle(curvaturesFilter.GetOutput().GetPointData().GetScalars().GetName());
scalarBar.SetNumberOfLabels(5);
vtkRenderer renderer = vtkRenderer.New();
renderer.AddActor(actor);
renderer.AddActor2D(scalarBar);
renderer.SetBackground(1, 1, 1);
vtkRenderWindow renderWindow = renderWindowControl.RenderWindow;
renderWindow.AddRenderer(renderer);
renderWindow.Render();
}
3)效果

4)说明
示例 PolyDataCurvature 实现了一个网格模型的曲率计算,并通过颜色映射表来显示模型表面的曲率。
首先读入一个 vtkPolyData 人脸模型数据,将其作为vtkCurvatures的输入,并调用SetCurvatureTypeToMaximum()函数来计算最大主曲率。其他三种曲率计算函数分别调用:

vtkCurvatures 支持四种曲率同时计算。在该类内部计算完曲率数据后,将其作为输出的vtkPolyData点的属性数据。保存属性数据时,四种曲率属性数据分别对应属性名字为Minimum Curvature、Maximum Curvature、Gauss Curvature和Mean Curvature,因此可以通过属性名字来获取相应的曲率数据。例如要获取高斯曲率数据,可调用:

为了能够在模型上显示曲率属性数据,这里通过颜色映射表来显示。定义一个256色的vtkLookupTable 对象,并设置曲率数据的范围。将该颜色映射表添加至 vtkPolyDataMappe中。另外,这里用到了一个新的 vkScalarBarActor 类,该类支持将一个颜色映射表转换为个 Actor 对象,将颜色表以图形的形式显示,并支持设置图形相应的名字和显示的数据 Label个数,这里显示5个数据 Label。设置完毕,将模型数据的Actor 和颜色表 Actor 对象添加至Renderer中可。
更多推荐

所有评论(0)