1.点法向量和单元法向量
三维平面的法向量是指垂直于该平面的三维向量。曲面在某点P处的法向量为垂直于该点切平面的向量。对于一个网格模型,其每一个点和单元都可以计算一个法向量,在三维计算机图形学中法向量一个重要应用是光照和阴影计算。对于网格模型,模型是有一定数量的面片(单元)来逼近的,面片越多,则模型越精细;反之,则越粗糙。在计算网格模型的法向量时,单元法向量计算比较简单,可以通过组成每个单元的任意两条边的叉乘向量并归一化来表示。而,对于点的法向量,则是由所有使用该点的单元法向量的平均值来表示。
VTK中计算法向量的Filter是vtkPolyDataNormals()。该类针对单元为 三角形或者多边形类型的vtkPolyData数据进行计算。由于法向量分为点法向量和单元法向量,可以通过函数SetComputeCellNormals()和SetComputePointNormals()来设置需要计算的法向量类型。
默认情况下计算点法向量,关闭单元法向量计算。
示例演示了一个vtkPolyData模型的点法向量和单位法向量的计算:
#include <vtkAutoInit.h> VTK_MODULE_INIT(vtkRenderingOpenGL); VTK_MODULE_INIT(vtkInteractionStyle); #include <vtkSmartPointer.h> #include <vtkPolyDataReader.h> #include <vtkPolyDataNormals.h> //计算法向量 #include <vtkMaskPoints.h> #include <vtkArrowSource.h> #include <vtkGlyph3D.h> #include <vtkPointData.h> #include <vtkProperty.h> // #include <vtkPolyDataMapper.h> #include <vtkActor.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> int main() { vtkSmartPointer<vtkPolyDataReader> plyReader = vtkSmartPointer<vtkPolyDataReader>::New(); plyReader->SetFileName("fran_cut.vtk"); plyReader->Update(); vtkSmartPointer<vtkPolyDataNormals> normFilter = vtkSmartPointer<vtkPolyDataNormals>::New(); normFilter->SetInputData(plyReader->GetOutput()); normFilter->SetComputePointNormals(1);//开启点法向量计算 normFilter->SetComputeCellNormals(0); //关闭单元法向量计算 normFilter->SetAutoOrientNormals(1); normFilter->SetSplitting(0); normFilter->Update(); vtkSmartPointer<vtkMaskPoints> mask = vtkSmartPointer<vtkMaskPoints>::New(); mask->SetInputData(normFilter->GetOutput()); mask->SetMaximumNumberOfPoints(300); mask->RandomModeOn(); mask->Update(); vtkSmartPointer<vtkArrowSource> arrow = vtkSmartPointer<vtkArrowSource>::New(); arrow->Update(); //一定要更新 否则数据没有添加进来,程序会报错 vtkSmartPointer<vtkGlyph3D> glyph = vtkSmartPointer<vtkGlyph3D>::New(); glyph->SetInputData(mask->GetOutput()); glyph->SetSourceData(arrow->GetOutput());//每一点用箭头代替 glyph->SetVectorModeToUseNormal();//设置向量显示模式和法向量一致 glyph->SetScaleFactor(0.01); //设置伸缩比例 glyph->Update(); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(plyReader->GetOutput()); vtkSmartPointer<vtkPolyDataMapper> normMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); normMapper->SetInputData(normFilter->GetOutput()); vtkSmartPointer<vtkPolyDataMapper> glyphMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); glyphMapper->SetInputData(glyph->GetOutput()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); vtkSmartPointer<vtkActor> normActor = vtkSmartPointer<vtkActor>::New(); normActor->SetMapper(normMapper); vtkSmartPointer<vtkActor> glyphActor = vtkSmartPointer<vtkActor>::New(); glyphActor->SetMapper(glyphMapper); glyphActor->GetProperty()->SetColor(1, 0, 0); double origView[4] = { 0, 0, 0.33, 1 }; double normView[4] = { 0.33, 0, 0.66, 1 }; double glyphView[4] = { 0.66, 0, 1, 1 }; vtkSmartPointer<vtkRenderer> origRender = vtkSmartPointer<vtkRenderer>::New(); origRender->SetViewport(origView); origRender->AddActor(actor); origRender->SetBackground(1, 0, 0); vtkSmartPointer<vtkRenderer> normRender = vtkSmartPointer<vtkRenderer>::New(); normRender->SetViewport(normView); normRender->AddActor(normActor); normRender->SetBackground(0, 1, 0); vtkSmartPointer<vtkRenderer> glyphRender = vtkSmartPointer<vtkRenderer>::New(); glyphRender->SetViewport(glyphView); glyphRender->AddActor(glyphActor); glyphRender->AddActor(normActor); glyphRender->SetBackground(0, 0, 1); vtkSmartPointer<vtkRenderWindow> rw = vtkSmartPointer<vtkRenderWindow>::New(); rw->AddRenderer(origRender); rw->AddRenderer(normRender); rw->AddRenderer(glyphRender); rw->SetWindowName("Calculating Point Norm & Cell Norm"); rw->SetSize(960, 320); rw->Render(); vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New(); rwi->SetRenderWindow(rw); rwi->Initialize(); rwi->Start(); return 0; }
输出结果如下图所示:
在计算法向量时需要注意一个问题,即法向量的方向。因为对于同一个平面来讲,可以有两个方向完全相反的法向量。一般是根据单元的点的顺序确定,采用右手定则来定义一个平面的法向量方向。因此计算平面方向量的时候,法向量的方向与单元的点的顺序密切相关。必须保持单元的点顺序一致,才会得到合理的法向量。当然,函数SetConsistency()可以设置自动调整模型的单元法向量。SetAutoOrientNormals()可以设置自动调整法线的方向。
2.关于类vtkGlyph3D
详细描述:为每个输入点拷贝相应方向和伸缩比例的轮廓几何体
vtkGlyph3d是一个过滤器,当拷贝几何体到每个输入点时,它起到滤波作用.glyph从过滤输入源中以多边形数据形式定义,glyph根据输入的矢量和法向量来确定方向,根据伸缩数据和矢量大小来确定伸缩比例.当glyph较多时,可能通过对象源与其相应的定义信息来创建glyph表.glyph表可以通过伸缩值或矢量大小来索引相应的gpyph对象.
要使用vtkGlyph3D对象,我们首先需要提供一个输入集和一个对象源来定义ghyph.然后决定是否对ghyph进行伸缩,以及怎样对其进行伸缩,接下来决定是否对glyph设置方向,以及如何根据矢量及法向量来设置它,最终决定我们是用glyph表还是仅仅是单一的ghyph.如果使用了glyph表,我们还需要考虑相应的索引值.vtkGlyph3D 实际上是一种符号化的算法工具,可以使用一个源(如球体)为输入数据集的每一个点生成一个符号,并且可以设置符号的方向以及缩放比例,简单点说就是对于你想关注的数据点添加符号标注,符号的样式由自己指定。比如你有一个曲面数据,希望将曲面数据的每个点都用锥体标注出来并且锥体的方向表示该点的法向量方向,这个时候就可以使用vtkGlyph3D。
3.本例的注意事项与经验谈
注意算法执行之后,要注意及时更新,Update()一下!!!
4. 符号化Glyphing再谈
前文提到一个事,就是用符号化操作显示单元的法向量。
模型的法向量数据是向量数据,因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通过符号化(Glyphing)技术将法向量图形化显示。Glyphing是一种基于图形的可视化技术,这些图像可以是简单的基本图形,如具有方向的椎体,也可以是更加复杂的图像。VTK中就是应用vtkGlyph3D类实现该功能的,并且可以支持Glyphing图形的缩放、着色、设置空间姿态等。使用该类时,需要接受两个输入:一个是需要显示的几何数据点集合;另一个是Glyph图形数据,为vtkPolyData数据。![]()
由于读入的模型数据比较大,点比较多,因此使用vtkMaskPoints类采样部分数据,该类保留输入数据中的点数据及其属性,并支持点数据的采样。为了减小计算量,随机采样了300个点做Glyphing显示。将其输出作为vtkGlyph3D的输入数据,而SetSourceData()设置了一个VTKArrowSource数据作为源数据,这样的效果是在输入数据的每一个点处会显示一个Glyph图形,这里我选用的就是箭头图形。vtkGlyph3D::SetVectorModeToUseNormal()指定要使用法向量数据来控制Glyph图形方向。vtkGlyph3D::SetScaleFactor()则控制Glyph图形的大小。