VTK知识学习(11)- 可视化管线
1、前言
可视化管线(Visualization Pipeline),用于获取或创建数据、处理数据以及数据写入文件或者把数据传递给渲染引擎进行显示。
比较图1和图 2的可视化管线结构,可以看出图2多了一个vtkMarchingCubes用于处理读入的数据。
VTK中把与类似vtkMarchingCubes 对数据做处理的类称为Filter。
综合图 1 和图 2,可以抽象出更一般的 VTK 可视化管线结构,如图 3 所示。
2、三要素
Source 是指用于创建数据(如vtkCylinderSource)或者读取数据(如 vtkBMPReader、vtkStructuredPointsReader 等)类的统称,即 VTK的数据源。
Source 输出的数据作为Filter 的输入,经 Filter 处理以后(可以经多个Filter 处理),生成新的数据。
Filter 的输出可以直接写入文件,或者经 Mapper 变换后传入渲染引擎进行渲染、显示,结束可视化管线。图3所示的箭头方向即为 VTK里数据流流动的方向。
可视化管线的三要素分别是数据对象、处理对象和数据流方向,Source、Filter 和Mapper 一起构成了处理对象,它们的区别是基于数据流的初始化、维持和终止。
根据数据的生成方式,Source可以分为程序源对象(Procedural,如vtkCylinderSource,通过程序代码生成相关的数据)和读取源对象(Reader,如 vtkBMPReader,从外部文件中导入数据)。
3、区别
Source 没有输入,但至少有一个输出;
Filter 可以有一个或多个输入,产生一个或多个输出;
Mapper 接受一个或多个输入,但没有输出,
写文件的Writer(如vtkBMPWriter)可以看作Mapper,负责把数据写入文件或者流(Stream)中,因此,Mapper是可视化管线的终点,同时也是可视化管线和渲染引擎(有时也称之为图形管线)的桥梁。
4、可视化管线的链接
由示例可知,可视化管线里各个模块的连接是通过接口SetlnputConnection()和 GetOutputPort()来完成的。即:
cubes.SetInputConnection(reader.GetOutputPort());
上行代码将 reader 的输出端口与 cubes 的输入端口建立连接。vtkMarchingCubes作为 Filter 只接受一个输入,Filter 概括起来有以下三种类型:
单个输入,产生单个输出;
多个输入,产生单个输出,但输出的数据可有多种用途,比如,读入数据后,可以对其作等值面提取,另外还可以针对读入的数据生成轮廓线(Outline);
单个输入,产生多个输出。
使用 SetInputConnection()和 GetOutputPort()连接可视化管线时,还要求连接的两部分之间的数据类型必须一致。由于管线是运行时才执行的,如果连接的两部分类型不匹配,程序运行时就会报错。比如,vtkMarchingCubes要求输入的是 vtkImageData 类型的数据,如果给它输入的是 vtkPolyData 类型的,程序运行时就会报错误。
5、可视化管线的执行
可视化管线连接完成后,必须有一种机制来控制管线的执行。有时,对某一部分数据做了改变,只希望改变的这部分数据在可视化管线里做更新,而不要影响其他没做改变的数据。
如图 所示,假如 Filter D的输入发生了变化,E和F是依赖于D的输入的,所以虚线框内的部分是需要重新执行的管线,而C和G是另外一个分支,D输入的改变不影响C和G,所以,为了节省运行时间,C和G是不需要重新执行的。毕竟对于三维的应用程序来说,处理的数据量一般都是比较大的,如果真能这样做,有利于提高程序的运行效率。
VTK 采用一种叫作“惰性赋值”(Lazy Evaluation)的方案来控制管线的执行,惰性赋值是指根据每个对象的内部修改时间来决定什么时候执行管线,只有当用户或者程序发出“请求数据”时,管线才会被执行。
vkObiect 类里有一个重要的成员变量MTime,管线里的每个从 vtkObject 派生的类的对象都会跟踪自己的内部修改时间,当遇到“请求数据”时,该对象会比较这个修改时间,如果发现修改时间发生了改变,对象就会执行。换言之,VTK是采用命令驱动(Demand Driven)的方法来控制管线的执行,这种方法的好处是,当对数据对象作了更改时,不必立即做计算,只有当发出请求时才开始处理,这样能最小化计算所需的时间,以便更流畅地与数据进行交互。
6、智能指针
智能指针会自动管理引用计数的增加与减少,若检测到某对象的引用计数值减少为0,则会自动释放该对象的资源,从而达到自动管理内存的目的。
VTK中创建一个对象可以用两种方法:
一种是使用vkObiectBase里的静态成员变量New(),用Delete()方法析构;
另一种就是示例中使用多次的智能指针vtkSmartPointer<T>。
当然,这个在c++中的了。……