“VTK图形图像开发进阶_张晓东_罗火灵”的学习笔记。
东灵工作室 教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686
VTK官网学习地址:https://vtk.org/doc/nightly/html/
法向量的定义
法向量,是空间解析几何的一个概念,垂直于平面的直线所表示的向量为该平面的法向量。法向量适用于解析几何。由于空间内有无数个直线垂直于已知平面,因此一个平面都存在无数个法向量(包括两个单位法向量)。
直线的法向量是与方向向量相垂直的向量。
三维平面的法向量是指垂直于该平面的三维向量。曲面在某点P处的法向量为垂直于该点切平面的向量。对于一个网格模型,其每一个点和单元都可以计算一个法向量,在三维计 算机图形学中法向量一个重要的应用是光照和阴影计算。对于网格模型,模型是由一定数量的面片(单元)来逼近的,面片越多,则模型越精细;反之,则越粗糙。
vtkPolyDataNormals
VTK中计算法向量的Filter为vtkPolyDataNormals该类针对单元为三角形或者多边形 类型的vtkPolyData数据进行计算。
由于法向量分为点法向量和单元法向量,
可以通过函数 SetComputeCellNormals()和SetComputePointNormals()
来设置需要计算的法向量类型。默认情 况下计算点法向量,关闭单元法向量计算。
模型的法向量数据是向量数据,因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通过符号化(Glyphing)技术将法向量图形化显示如下图右边的面具效果。
计算法向量前后的模型显示效果图。从图中可以看出,加入法向量后,模型显 示得更加平滑,而未计算法向量的模型看起来比较粗糙。
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//测试文件:../data/fran_cut.vtk
int main(int argc, char *argv[])
{
vtkNew<vtkPolyDataReader> reader;
reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap06/data/fran_cut.vtk");
reader->Update();
/*
VTK中计算法向量的Filter为vtkPolyDataNormals»该类针对单元为三角形或者多边形 类型的vtkPolyData数据进行计算。
由于法向量分为点法向量和单元法向量,
可以通过函数 SetComputeCell Normals()和SetComputePointNormals。
来设置需要计算的法向量类型。默认情况下计算点法向量,关闭单元法向量计算。
*/
vtkNew<vtkPolyDataNormals> normfilter;
normfilter->SetInputData(reader->GetOutput());
normfilter->SetComputePointNormals(1); //开启计算点法向量
normfilter->SetComputeCellNormals(0); //关闭单元法向量计算
normfilter->SetAutoOrientNormals(1); //设置自动调整法向量
//vtkPolyDataNormals默认开启对锐边缘(Sharp Edge)的处理。
//如果检测到存在锐边缘,则会将其分裂,因此模型的数据可能会发生变化。
//可以通过vtkPolyDataNormals::SetSplitting。函数关闭该功能
normfilter->SetSplitting(0);
normfilter->Update();
//模型上随机生成10+1个点
vtkNew<vtkMaskPoints> mask;
mask->SetInputData(normfilter->GetOutput());
mask->SetMaximumNumberOfPoints(10);
mask->RandomModeOn();
mask->Update();
//在随机生成的点上 加上箭头方向
vtkNew<vtkArrowSource> arrow;
arrow->Update();
vtkNew<vtkGlyph3D> glyph;
glyph->SetInputData(mask->GetOutput()); //设置点数据位置
glyph->SetSourceData(arrow->GetOutput()); //设置源数据
glyph->SetVectorModeToUseNormal(); //知道要使用法向量数据来控制glyph图形的方向
glyph->SetScaleFactor(0.05); //箭头图形长度
glyph->Update();
//原始图
vtkNew<vtkPolyDataMapper> originmapper;
originmapper->SetInputData(reader->GetOutput());
vtkNew<vtkActor> originactor;
originactor->SetMapper(originmapper);
//法向量计算后
vtkNew<vtkPolyDataMapper> normedmapper;
normedmapper->SetInputData(normfilter->GetOutput());
vtkNew<vtkActor> normedactor;
normedactor->SetMapper(normedmapper);
//显示几个法向量的图
vtkNew<vtkPolyDataMapper> glyphmapper;
glyphmapper->SetInputData(glyph->GetOutput());
vtkNew<vtkActor> glyphactor;
glyphactor->SetMapper(glyphmapper);
glyphactor->GetProperty()->SetColor(1., 0.,0.);
double originalviewport[4] = {0.0, 0.0, 0.33, 1.0};
double normviewport[4] = {0.33, 0.0, 0.66, 1.0};
double glphviewport[4] = {0.66, 0.0, 1.0, 1.0};
vtkNew<vtkRenderer> originalrenderer;
originalrenderer->SetViewport(originalviewport);
originalrenderer->AddActor(originactor);
originalrenderer->ResetCamera();
originalrenderer->SetBackground(1.0, 1.0, 1.0);
vtkNew<vtkRenderer> normedrenderer;
normedrenderer->SetViewport(normviewport);
normedrenderer->AddActor(normedactor);
normedrenderer->ResetCamera();
normedrenderer->SetBackground(1.0, 1.0, 1.0);
vtkNew<vtkRenderer> glyphRenderer;
glyphRenderer->SetViewport(glphviewport);
glyphRenderer->AddActor(glyphactor);
glyphRenderer->AddActor(normedactor);
glyphRenderer->ResetCamera();
glyphRenderer->SetBackground(1.0, 1.0, 1.0);
vtkNew<vtkRenderWindow> renderwindow;
renderwindow->AddRenderer(originalrenderer);
renderwindow->AddRenderer(normedrenderer);
renderwindow->AddRenderer(glyphRenderer);
renderwindow->SetSize(640, 320);
renderwindow->Render();
renderwindow->SetWindowName("PolyDataNormal");
vtkNew<vtkRenderWindowInteractor> renderwindowinteractor;
renderwindowinteractor->SetRenderWindow(renderwindow);
renderwindowinteractor->Initialize();
renderwindowinteractor->Start();
return EXIT_SUCCESS;
}
VTK中的vtkCurvatures类实现了四种计算网格模型点曲率的计算方法。该类接受一个 vtkPolyData数据,将计算得到的曲率数据作为网格模型的点的属性数据存入返回的 vtkPolyData中。
实例:一个网格模型的曲率计算,并通过颜色映 射表来显示模型表面的曲率
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//测试文件:../data/fran_cut.vtk
int main(int argc, char *argv[])
{
vtkSmartPointer<vtkPolyDataReader> reader =
vtkSmartPointer<vtkPolyDataReader>::New();
reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap06/data/fran_cut.vtk");
reader->Update();
vtkSmartPointer<vtkCurvatures> curvaturesFilter =
vtkSmartPointer<vtkCurvatures>::New();
curvaturesFilter->SetInputConnection(reader->GetOutputPort());
//curvaturesFilter->SetCurvatureTypeToMinimum();
curvaturesFilter->SetCurvatureTypeToMaximum(); //计算最大主曲率
//curvaturesFilter->SetCurvatureTypeToGaussian();
//curvaturesFilter->SetCurvatureTypeToMean();
curvaturesFilter->Update();
//获取高斯曲率数据
//四种曲率属性数据分别对应属性名字为 Minimum_Curvature、Maximum_Curvature、Gauss_Curvature和Mean_Curvature
vtkDataArray *gauss = static_cast<vtkDataArray*>(curvaturesFilter->GetOutput()->GetPointData()->GetArray("Gauss_Curvature"));
double scalarRange[2];
curvaturesFilter->GetOutput()->GetScalarRange(scalarRange);
vtkSmartPointer<vtkLookupTable> lut =
vtkSmartPointer<vtkLookupTable>::New();
lut->SetHueRange(0.0,0.6);
lut->SetAlphaRange(1.0,1.0);
lut->SetValueRange(1.0,1.0);
lut->SetSaturationRange(1.0,1.0);
lut->SetNumberOfTableValues(256);
lut->SetRange(scalarRange); //设置范围
lut->Build();
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(curvaturesFilter->GetOutput());
mapper->SetLookupTable(lut);
mapper->SetScalarRange(scalarRange);
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
//vtkScalarBarActor类,该类支持将一个颜色映射表转换为一个Actor对象
//将颜色表以图形的形式显示,并支持设置图形相应的名字和显示的数据Label个数
vtkSmartPointer<vtkScalarBarActor> scalarBar =
vtkSmartPointer<vtkScalarBarActor>::New();
scalarBar->SetLookupTable(mapper->GetLookupTable());
scalarBar->SetTitle(curvaturesFilter->GetOutput()->GetPointData()->GetScalars()->GetName());
scalarBar->SetNumberOfLabels(5);
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(actor);
renderer->AddActor2D(scalarBar);
renderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
renderWindow->SetSize(640, 480);
renderWindow->Render();
renderWindow->SetWindowName("PolyDataCurvature");
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindow->Render();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
现代扫描技术的发展使得获取点云数据不再困难,通过曲线重建技术可以获取表面网格来表示各种复杂的实体。但是点云数据中往往存在噪声,这样得到的重建网格通常都需要进行平滑处理。
拉普拉斯平滑是一种常用的网格平滑算法。该方法的原理比较简单,如下图所示:
将每个点用其邻域点的中心来代替。通过不断地迭代,可以得到较为光滑的网格。
VTKSmoothPolyDataFilter类实现了网格的拉普拉斯平滑算法,使用方法如下:
参考:https://blog.csdn.net/weixin_38293453/article/details/104689138
#include
#include
VTK_MODULE_INIT(vtkRenderingOpenGL2)
VTK_MODULE_INIT(vtkInteractionStyle)
VTK_MODULE_INIT(vtkRenderingFreeType)
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
vtkSmartPointer<vtkPolyDataReader> reader =
vtkSmartPointer<vtkPolyDataReader>::New();
reader->SetFileName("C:/Users/jbyyy/Desktop/work/QTDEMO/jbyyy/VTK/学习资料/VTK图形图像开发进阶+张晓东+PDF+源码/VTK图形图像开发进阶随书代码/VTK图形图像开发进阶随书代码/Examples/Chap06/data/fran_cut.vtk");
reader->Update();
vtkSmartPointer<vtkSmoothPolyDataFilter> smoothFilter =
vtkSmartPointer<vtkSmoothPolyDataFilter>::New();
smoothFilter->SetInputConnection(reader->GetOutputPort());
smoothFilter->SetNumberOfIterations(300); //迭代多少此 拉普拉斯平滑
//其实在该类中还有多个变量来控制平滑过程,利用这些变量在一定程度上可以控制细节的损失。
smoothFilter->BoundarySmoothingOff(); //控制是否对边界点进行平滑
smoothFilter->FeatureEdgeSmoothingOn(); //是否对特征边上的点进行平滑
smoothFilter->Update();
//虽然通过特征边平滑设置可以降低一部分细节损失,
//但并不能完全避免,且随着laplace平滑的不断迭代,
//模型会逐渐向网格的中心收缩。
//所以,vtkWindowSincPolyDataFilter是一种更好的选择
//该算法采用窗口Sinc函数实现网格平滑,能够最小程度地避免收缩
// vtkSmartPointer wndSincSmoothFilter =
// vtkSmartPointer::New();
// wndSincSmoothFilter->SetInputConnection(reader->GetOutputPort());
// wndSincSmoothFilter->SetNumberOfIterations(100);
// wndSincSmoothFilter->Update();
vtkSmartPointer<vtkPolyDataMapper> inputMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
inputMapper->SetInputConnection(reader->GetOutputPort());
vtkSmartPointer<vtkActor> inputActor =
vtkSmartPointer<vtkActor>::New();
inputActor->SetMapper(inputMapper);
vtkSmartPointer<vtkPolyDataMapper> smoothedMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
smoothedMapper->SetInputConnection(smoothFilter->GetOutputPort());
vtkSmartPointer<vtkActor> smoothedActor =
vtkSmartPointer<vtkActor>::New();
smoothedActor->SetMapper(smoothedMapper);
double leftViewport[4] = { 0.0, 0.0, 0.5, 1.0 };
double rightViewport[4] = { 0.5, 0.0, 1.0, 1.0 };
vtkSmartPointer<vtkRenderer> leftRenderer =
vtkSmartPointer<vtkRenderer>::New();
leftRenderer->SetViewport(leftViewport);
leftRenderer->AddActor(inputActor);
leftRenderer->SetBackground(0.2, 0, .5);
leftRenderer->ResetCamera();
vtkSmartPointer<vtkRenderer> rightRenderer =
vtkSmartPointer<vtkRenderer>::New();
rightRenderer->SetViewport(rightViewport);
rightRenderer->AddActor(smoothedActor);
rightRenderer->SetBackground(0.5, 0.5, 0);
rightRenderer->ResetCamera();
vtkSmartPointer<vtkRenderWindow> rw =
vtkSmartPointer<vtkRenderWindow>::New();
rw->AddRenderer(leftRenderer);
rw->AddRenderer(rightRenderer);
rw->SetSize(640, 320);
rw->SetWindowName("PolyData Grid Smooth By LapLasian");
vtkSmartPointer<vtkRenderWindowInteractor> rwi =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
rwi->SetRenderWindow(rw);
rwi->Initialize();
rwi->Start();
return 0;
}