前言: 解决是什么样的数据应用到 VTK 可以显示出模型的问题;夹杂大量的个人理解,可能有偏差。
参考资料:
《An Object-Oriented Approach to 3D Graphics》
《The VTK User’s Guide》
上节实例中使用了vtk中自带类 vtkConeSource
通过设定参数实例化了一个锥体。锥体数据是什么样的?可否自己设定类似格式的数据,从而生成想要的实体?
从最简单问题入手,思考如何显示一个三角形 ?
首先需要定义三个点,三个点可以用坐标表示;其次把点按一定次序连接后就形成了三角形。坐标表示比较好办;按次序连接,次序如何规定?对于三角形而言,无非就是顺时针或者逆时针。为了方便,可以在设定点的时候为点设定一个 ID,在连接时,只需按 ID 逐次连接即可。
如果这是个类似于 *.stl 文件中的三角面片呢,即除了三角形结构外,每个三角形还有法向量数据(又如最简单的颜色数据、具有物理含义的数据等)。解决方案很简单,只需在定义好三角形点坐标和连接顺序后,在加入一个额外数据即可。而这三部分的数据内容就可生成一个可视化的模型了。
上述的一些过程,在 VTK 中有规范的名词描述。
定义一个个基础点坐标,在 VTK 中就是定义 几何结构(Geometry) ,在 VTK 中用点表示,使用 vtkPoints
。
定义点按顺序的连接,在 VTK 中就是定义 拓扑结构(Topology)。在 VTK 中用 拓扑结构类型和节点 表示。
拓扑结构实际上分两个层次,一种是单元的拓扑结构(称作单元 Cell),一种是整个数据集的拓扑结构(用 CellArray 表示)。两者数据结构不同。
为什么会分两种,为何不直接用数据集拓扑结构表示?
一般模型(DataSet)都是由复杂的结构组成,这些复杂结构在 VTK 中不可能有直接对应的 拓扑结构类型,但却可以由一个个简单的拓扑结构(称作单元 Cell)堆积而成。常见简单拓扑结构(Cell)包括 点、线、三角形、四面体等,而这些简单拓扑结构(单元Cell)组合就形成了数据集的拓扑结构(CellArray)。
单元拓扑结构(Cell)与数据集拓扑结构(CellArray)数据储存方式也不同。
具体数据表达方式的异同,先按下不表,先将名词描述完毕,本节结尾以一个例子详细列出其储存方式。
几何结构 (点 points) 和 拓扑结构 (单元集 CellArray) 组成了组织结构(没有属性数据的数据集)。如上所述,此时组织结构数据已经可以用于VTK,并最终渲染出模型了。
属性数据. 用于描述 点 或 单元(包含 Cell 和 CellArray)的属性, 一般用 标量或张量等。 常用类是 vtkPointData 和 vtkCellData。
组织结构 与 属性数据 便组成了我们常用的数据集(DataSet)。
整个数据集构建用如下图表示
极其重要的两点认知:
Cell 根据类型以及点ID确定。不同单元类型产生不同单元的拓扑结构。
DataSet 是一系列包含组织结构数据和属性数据的集合,不同的组织结构数据需要使用不同类型的数据集。如对于二维和三维数据,其数据类型肯定是不一样的,因此也要指定与组织结构对应的数据集,从而接收到相应的组织结构和属性数据。最终才能准确无误的渲染出模型。
所以,下面(第二节中)也将有两种数据类型要介绍:单元(Cell)类型和数据集(DataSet)类型。
根据以上理论,先实现一个简单例子: 构建一个点、线段、三角形,用 vtk 显示出来。
代码
./demo.cxx
#include
#include
#include // 点单元
#include // 线单元
#include // 三角形单元
#include // 四面体单元
#include
#include
#include
#include
#include
#include
#include
#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2); // VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);
int main()
{
/* 设定集合结构 */
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// 组成点单元的点
points->InsertNextPoint(0,0,0);
// 组成线单元的点
points->InsertNextPoint(0,-1,0);
points->InsertNextPoint(1,-1,0);
// 组成三角形单元的点
points->InsertNextPoint(1,0,0);
points->InsertNextPoint(0,1,0);
points->InsertNextPoint(0,0,1);
/* 设定单元的拓扑结构 */
// 设定点单元的拓扑结构
vtkSmartPointer<vtkVertex> vertex = vtkSmartPointer<vtkVertex>::New();
vertex->GetPointIds()->SetId(0,0);
// 设定线单元的拓扑结构
vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New();
line->GetPointIds()->SetId(0,1);
line->GetPointIds()->SetId(1,2);
// 设定三角形单元的拓扑结构
vtkSmartPointer<vtkTriangle> triangle = vtkSmartPointer<vtkTriangle>::New();
triangle->GetPointIds()->SetId(0,3);
triangle->GetPointIds()->SetId(1,4);
triangle->GetPointIds()->SetId(2,5);
/* 所有单元组成数据集单元(数据集的拓扑结构) */
vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();
// 方法1 : 直接插入上面构建的 cell 对象
cellArray->InsertNextCell(vertex);
//cellArray->InsertNextCell(line);
//cellArray->InsertNextCell(triangle);
// 方法2 : 前者为点数量,后者为点 id 列表
vtkIdType pts[2] = {
1,2};
cellArray->InsertNextCell(2,pts);
// 方法3 : 直接输入点 id 列表
cellArray->InsertNextCell({
3,4,5});
/* 几何结构和拓扑结构,构成数据集(ps:没有设定属性数据) */
vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
polydata->SetPoints(points);
polydata->SetVerts(cellArray);
polydata->SetLines(cellArray);
polydata->SetPolys(cellArray);
/* 以下,可视化管线进行显示 */
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(polydata);
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
vtkSmartPointer<vtkRenderer> ren = vtkSmartPointer<vtkRenderer>::New();
ren->AddActor(actor);
vtkSmartPointer<vtkRenderWindow> win = vtkSmartPointer<vtkRenderWindow>::New();
win->AddRenderer(ren);
vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();
iren->SetRenderWindow(win);
win->Render();
iren->Start();
ren->Delete();
win->Delete();
iren->Delete();
return 0;
}
结果
几点说明
在构建几何结构,插入点的时候,自动标定点 ID 从 0 开始。且如 points->InsertNextPoint(0,0,0)
是有返回值的,返回值就为点的 ID (注意:此处返回值 ID 不是 int 类型)。
上面在构建数据集拓扑结构时,对于点、线、三角形,分别展示了三种不同方法。都可行。
具体可参考:vtkCellArray Class Reference
/* 所有单元组成数据集单元(数据集的拓扑结构) */
vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();
// 方法1 : 直接插入上面构建的 cell 对象
cellArray->InsertNextCell(vertex);
// 方法2 : 前者为点数量,后者为点 id 列表
vtkIdType pts[2] = {
1,2};
cellArray->InsertNextCell(2,pts);
// 方法3 : 直接输入点 id 列表
cellArray->InsertNextCell({
3,4,5});
点、线段、三角形的单元表示,都被包含于多边形类型的数据集中,所以可以用一个数据集 vtkPolyData
来表示。事实上, PolyData
数据集类型,就是指包含点、线、多边形的数据集合,因此才有对应的下列函数
polydata->SetVerts(); // 加入点
polydata->SetLines(); // 加入线
polydata->SetPolys(); // 加入多边形
关于 Cell
与 CellArray
先给出上述例子的 cell
和 cellArray
的数据结构
cell topology:
---------
Cell 0: Vertex | point ids: {0}
Cell 1: Line | point ids: {1, 2}
Cell 2: Triangle | point ids: {3, 4, 5}
vtkCellArray (current):
-----------------------
Offsets: {0, 1, 3, 6}
Connectivity: {0, 1, 2, 3, 4, 5}
cell 部分很好理解,就是指定类型后再加上相应点ID,然后顺次连接。
cellArray 储存了所有的 cell。其中包含两个列表数据 offsets
和 connectivity
。
具体依旧可参考:vtkCellArray Class Reference
按照上面总结的图片,依次说明
几行代码即可说明,就是通过指定点坐标确定一系列点。使用 vtkPoints
#include
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// 获取返回值,即点的 ID
vtkIdType pid[1];
pid[0] = points->InsertNextPoint(0,0,0);
// 或者不使用返回值,默认 ID 从 0 编号
points->InsertNextPoint(0,0,0);
这里主要是单元的拓扑结构,即 Cell。前面已经指出单元拓扑结构是由单元类型和点ID组成。
上面已经使用了点、线、三角形的 Cell 类型,并给出了如何指定点 ID 的方法。如上述中对三角形而言
vtkSmartPointer<vtkTriangle> triangle = vtkSmartPointer<vtkTriangle>::New();
triangle->GetPointIds()->SetId(0,3);
triangle->GetPointIds()->SetId(1,4);
triangle->GetPointIds()->SetId(2,5);
SetId(Num1, Num2)
中, Num1
指明点在三角形中的编号,Num2
指明使用vtkPoints
点的 id。
不同的单元类型,通过给定点,生成不同结构。因此,重点在于都有那些单元(cell)类型上。事实上,VTK中单元类型可以分为线性与非线性两大类。以下图均来自于官方指导书 《An Object-Oriented Approach to 3D Graphics - Chapter5》
另外一个重要点,前述,对于一个三角形而言,事实上有两种连接顺序,即顺时针和逆时针。VTK 三角形类型单元使用了逆时针连接。如下图中 © 所示。所以当指明了一个 Cell 类型,其点的顺序在 VTK 中已经按一定顺序确定了。
下面是VTK中线性类型 单元
以及非线性类型 单元
各单元的具体属性,同样可参考官方书《An Object-Oriented Approach to 3D Graphics - Chapter5》
可以看出,非线性往往是在线性的基础上增加了点,使得线变弯曲。严格说,两者是在插值上的区别。
本质上就是在一些点、单元上的一个值。使用相应的函数,在特定点(可根据ID确定)添加,读取属性值即可。属性值类型及应用常分为以下几种
如前所述,数据集是可以输入到 VTK 可视化管线中,从而渲染出具体模型。
而对于不同类型的单元,适用的数据集不同。因此需要选择适当的数据集。在 VTK 中常用数据集有下
对于各个数据集的具体描述,同样见官方指导书。或使用时,具体查询。较为常用的是 ImageData、PolyData
,即用于表示图片数据和多边形数据。
vtkPoints
和 vtkVertex
的区别。