对比VTK中的不同Spline样条

Spline样条作用:

VTK中的Spline样条可以将顶点连接成平滑的线段,并且对线段重新采样。目前主要有vtkSCurveSpline、vtkKochanekSpline、vtkCardinalSpline和vtkParametricSpline四种Spline可以用,后文将对比这四种Spline的效果。

输入的点集

后文的示例都是用同样的四个单独顶点来完成,顶点的顺序可以更改,而Spline是严格根据点集中顶点的顺序来完成连接的,所以调换顶点顺序可以更改Spline的模样。

//定义点集
vtkPoints* ps = vtkPoints::New();
double a[3] = { 0,0,0 };    //插入顶点a
ps->InsertNextPoint(a);
double b[3] = { 1,0,0 };    //插入顶点b
ps->InsertNextPoint(b);
double c[3] = { 1,1,0 };    //插入顶点c
ps->InsertNextPoint(c);
double d[3] = { 0,1,0 };    //插入顶点d
ps->InsertNextPoint(d);
//将顶点插入到vtkPolyData中,没有定义拓扑
vtkNew<vtkPolyData> pointData;
pointData->SetPoints(ps);

vtkKochanekSpline:根据点集的顺序就能连接得到平滑曲线

//代码参考官方示例
vtkNew<vtkKochanekSpline> xSpline;
vtkNew<vtkKochanekSpline> ySpline;
vtkNew<vtkKochanekSpline> zSpline;

vtkNew<vtkParametricSpline> spline;
spline->SetXSpline(xSpline);
spline->SetYSpline(ySpline);
spline->SetZSpline(zSpline);
spline->SetPoints(ps);

vtkParametricFunctionSource* functionSource = vtkParametricFunctionSource::New();
functionSource->SetParametricFunction(spline);
functionSource->SetUResolution(50 * ps->GetNumberOfPoints());
functionSource->SetVResolution(50 * ps->GetNumberOfPoints());
functionSource->SetWResolution(50 * ps->GetNumberOfPoints());
functionSource->Update();
vtkPolyData* out = functionSource->GetOutput();

splines样条函数_游戏引擎


splines样条函数_图形渲染_02

vtkKochanekSpline生成的曲线弯曲幅度会相对小一点。

vtkCardinalSpline

vtkSmartPointer<vtkCardinalSpline> xSpline = 
		 vtkSmartPointer<vtkCardinalSpline>::New();
vtkSmartPointer<vtkCardinalSpline> ySpline =
		 vtkSmartPointer<vtkCardinalSpline>::New();
vtkSmartPointer<vtkCardinalSpline> zSpline = 
		 vtkSmartPointer<vtkCardinalSpline>::New();

vtkSmartPointer<vtkParametricSpline> spline =
    vtkSmartPointer<vtkParametricSpline>::New();
spline->SetXSpline(xSpline);
spline->SetYSpline(ySpline);
spline->SetZSpline(zSpline);
spline->SetPoints(ps);

vtkParametricFunctionSource* functionSource =
    	 vtkParametricFunctionSource::New();
functionSource->SetParametricFunction(spline);
functionSource->SetUResolution(50 * ps->GetNumberOfPoints());
functionSource->SetVResolution(50 * ps->GetNumberOfPoints());
functionSource->SetWResolution(50 * ps->GetNumberOfPoints());
functionSource->Update();
vtkPolyData* out = functionSource->GetOutput();

splines样条函数_图形学_03


splines样条函数_游戏引擎_04


vtkCardinalSpline生成曲线的弯曲幅度会相对更大一点。

vtkParametricSpline:更简单的构建曲线

vtkNew<vtkParametricSpline> spline;
spline->SetPoints(ps);

vtkParametricFunctionSource* functionSource = 
			vtkParametricFunctionSource::New();
functionSource->SetParametricFunction(spline);
functionSource->SetUResolution(50 * ps->GetNumberOfPoints());
functionSource->SetVResolution(50 * ps->GetNumberOfPoints());
functionSource->SetWResolution(50 * ps->GetNumberOfPoints());
functionSource->Update();
vtkPolyData* out = functionSource->GetOutput();

splines样条函数_图形渲染_05

splines样条函数_图形学_06


vtkParametricSpline生成曲线的效果目前看来和vtkCardinalSpline类似,实际上vtkParametricSpline默认使用的就是vtkCardinalSpline方法

vtkSCurveSpline:直接就是用直线相连

vtkNew<vtkSCurveSpline> xSpline;
  vtkNew<vtkSCurveSpline> ySpline;
  vtkNew<vtkSCurveSpline> zSpline;

  vtkNew<vtkParametricSpline> spline;
  spline->SetXSpline(xSpline);
  spline->SetYSpline(ySpline);
  spline->SetZSpline(zSpline);
  spline->SetPoints(ps);

  vtkParametricFunctionSource* functionSource = 
  			vtkParametricFunctionSource::New();
  functionSource->SetParametricFunction(spline);
  functionSource->Update();
  vtkPolyData* out = functionSource->GetOutput();

splines样条函数_图形渲染_07

splines样条函数_游戏引擎_08


如果需要用直线连接点集合的话,vtkSCurveSpline很合适。

注意点

如果你的顶点数量很大的话,要用以上Filter连接顶点前,注意要先对顶点排序,目前VTK还没有能直接用来对顶点集合排序的函数,因此基本需要自行实现。

vtkFrenetSerretFrame 弗莱纳框架

这个函数要使用的话需要额外的第三方依赖:

//在编译VTK时的CMakeList文件中或者CMAKE界面中设置
 ''Module_SplineDrivenImageSlicer:BOOL=ON'' or 
 ''VTK_MODULE_ENABLE_VTK_SplineDrivenImageSlicer:STRING=WANT''

主要作用:能够输入一组线段(例如上文代码段中的数据输出out),然后经过该Filter的处理,能够得到该线段在各顶点处的法向量和切向量属性数据。

// Create the frame
 vtkNew<vtkFrenetSerretFrame> frame;
 //functionSource为上文代码段中的生成曲线的结果
 frame->SetInputConnection(functionSource->GetOutputPort()); 
 frame->ConsistentNormalsOn();
 frame->Update();

 vtkNew<vtkGlyph3D> glyph3DNormals;
 vtkNew<vtkGlyph3D> glyph3DTangents;
 vtkNew<vtkGlyph3D> glyph3DBinormals;

 // for each vector, create a Glyph3D and DeepCopy the output
 double radius = 0.05;
 frame->GetOutput()->GetPointData()->SetActiveVectors("FSNormals");
 MakeGlyphs(frame->GetOutput(), radius, glyph3DNormals.GetPointer());
 vtkNew<vtkPolyData> normalsPolyData;
 normalsPolyData->DeepCopy(glyph3DNormals->GetOutput());

 frame->GetOutput()->GetPointData()->SetActiveVectors("FSTangents");
 MakeGlyphs(frame->GetOutput(), radius, glyph3DTangents.GetPointer());
 vtkNew<vtkPolyData> tangentsPolyData;
 tangentsPolyData->DeepCopy(glyph3DTangents->GetOutput());

 frame->GetOutput()->GetPointData()->SetActiveVectors("FSBinormals");
 MakeGlyphs(frame->GetOutput(), radius, glyph3DBinormals.GetPointer());
 vtkNew<vtkPolyData> binormalsPolyData;
 binormalsPolyData->DeepCopy(glyph3DBinormals->GetOutput());

将各属性渲染出来如下图所示:

splines样条函数_splines样条函数_09