第03章-VTK系统概述(1)
【译者:这个系列教程是以Kitware公司出版的《VTK User’s Guide -11th edition》一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934-23-8),因为时间关系,我们不能保证每周都能更新本书内容,但尽量做到一周更新一篇到两篇内容。敬请期待^_^。欢迎转载,另请转载时注明本文出处,谢谢合作!同一时候,因为译者水平有限,出错之处在所难免,欢迎指出订正!
】
【本节相应原书中的第19页至第25页】
本章旨在介绍VTK系统的整体概述,并解说运用C++、Java、Tcl和Python等语言进行VTK应用程序开发时所需掌握的基本知识。
首先我们从VTK系统的基本概念和对象模型抽象開始进行介绍。并在本章最后通过样例演示这些概念以及介绍一下在构建VTKproject时所须要掌握的知识。
3.1系统结构
VTK系统由两个子系统组成:一个是编译的C++类库,还有一个是解释性语言的封装层。以供Java、Tcl和Python等语言来操作该C++类库,系统结构如图3-1所看到的。
图3-1VTK系统由C++类库内核以及解释性语言(Java、Tcl、Python)封装层所组成。
该结构的长处在于保持解释性语言高速开发特性(避免编译、链接流程,工具简单而强大,同一时候又易于使用的GUI工具)的同一时候,用户能够利用编译的C++语言开发高效的算法(不管是CPU利用还是内存的利用)。当然,对于熟练掌握C++语言或者使用对应开发工具的用户来说。VTK应用程序能够全然採用C++语言进行开发。
因为VTK系统採用了面向对象的思想,因此利用VTK进行高效开发的关键在于深入理解系统内部的对象模型。以便消除用户在使用大量的VTK系统对象时的迷惑。并能更有效的对这些对象进行组合创建应用程序。此外。还须要了解系统中很多对象的功能,而这仅仅能通过阅读样例代码和在线文档来获得。
这本《VTK用户指南》中。我们将尽力介绍一些实用的VTK对象的组合以便用户能够将其应用到自己的应用程序中。
本章接下来将介绍VTK中的两个重要组件:可视化管线【译者:Visualization Pipeline,有些翻译成“可视化管道”,本书我们翻译成“可视化管线”】和渲染引擎。可视化管线主要负责数据获取或者创建,数据处理,然后将数据写入文件或者传递至渲染引擎中进行显示。而渲染引擎负责创建传递过来的数据的一个可视表达。
注意这里所提到的组件并不是VTK系统结构中详细的组件,而是一个抽象的概念组件。
本章是在比較高的层次上进行阐述。可是当你将本章的内容和下一章节中详细的样例或者是VTK源文件里所提供的大量的演示样例程序结合在一起时,你将会对本章所介绍的概念有更深刻的理解。
底层对象模型
vtkObject是VTK对象继承关系树的根结点,差点儿全部的VTK对象都继承自该类。除了一部分特殊对象继承自vtkObject的父类vtkObjectBase。全部的VTK对象都必须由对象的New()方法创建,并由对象的Delete()方法销毁。因为VTK对象中的构造函数被声明为受保护类型,因此VTK对象不可以在栈中分配空间。另外,VTK对象採用了共同基类和统一的创建、销毁方法,可以实现很多主要的面向对象操作。
引用计数。VTK对象内部显式地记录了引用本身的指针的个数。当一个对象通过静态的New()函数创建时。该对象内部的初始引用计数即为1。由于所创建的对象会有一个原始指针(Raw Pointer)指向该对象。
vtkObjectBase*obj = vtkExampleClass::New();
当指向某个对象的其它对象创建或者销毁时,引用计数会通过Register()和Unregister()函数进行对应的添加和降低。通常情况下系统会通过对象的API函数“Set()”自己主动完毕。
otherObject->SetExample(obj);
这时对象obj的引用计数值为2。由于除了原始指针之外,还有另外一个对象otherObject内部的指针指向它。当原始存储对象的指针不再须要时。通过Delete()函数能够删除该引用,即:
obj->Delete();
这样再利用原始指针去訪问该对象时将不再是安全的,由于该指针已经不再拥有对象的引用。因此为了保证对对象引用的有效管理,每次调用New()后都要进行Delete(),确保没有泄漏引用。
第二种避免引用泄漏的方法是通过类模板vtkSmartPointer<>提供的智能指针来简化对象的管理操作,上述样例能够重写为例如以下代码:
vtkSmartPointer<vtkObjectBase>obj = vtkSmartPointer<vtkObjectBase>::New();
otherOject->SetExample(obj);
该例中。智能指针会自己主动管理对象的引用。当智能指针变量超出其作用域而且不再使用时,比如当其为函数内部局部变量。函数返回时。智能指针会自己主动通知对象降低引用计数。
因为智能指针提供了内部静态New()函数,因此不须要原始指针来保存对象的引用,因此也不须要再调用Delete()函数。
执行时类型信息。
在C++中对象的实际类型可能与该对象的索引指针的类型不一致。
VTK中全部的类都有一个接口函数提供类名标识符,因此一个string类型足以标识这些类。执行时对象的实际类型能够通过GetClassName()函数获得:
constchar* type = obj->GetClassName();
通过IsA()函数能够推断一个对象是否是某一个类的实例,或者是该类的某个子类的实例:
if(obj->IsA(“vtkExampleClass”)) { … }
父类类型的指针能够通过静态函数SafeDownCast()安全地强制转换为子类类型的指针:
vtkExampleClass*example = vtkExampleClass::SafeDownCast(obj);
上述代码仅仅有当对象example是obj的子类实例时,类型转换才成功;否则返回空指针。
对象状态显示。easy理解的对象的当前状态信息对于程序的调试是十分实用的。VTK对象的状态能够通过输出函数Print()获得:
obj->Print(cout);
渲染引擎
组成VTK渲染引擎的类主要负责接收可视化管线(Visualization Pipeline)的输出数据并将结果渲染到窗体中。该过程主要涉及到下述一些组件。注意,这些仅仅是VTK渲染系统中比較经常使用的组件,并不是所有。
而每一个子标题仅仅是代表一个对象类型的最高层VTK超类【译者:或者称为“父类”】,在很多情况下,这些超类仅仅是定义了基本API函数的抽象类。而真正的实现则由其子类来完毕。
vtkProp。渲染场景中数据的可视表达(Visible Depictions)是由vtkProp的子类负责。
三维空间中渲染对象最经常使用的vtkProp子类是vtkActor和vtkVolume,当中vtkActor用于表示场景中的几何数据(Geometry Data),vtkVolume表示场景中的体数据(Volumetric Data)。vtkActor2D经常使用来表示二维空间中的数据。
vtkProp的子类负责确定场景中对象的位置、大小和方向信息。
控制Prop【译者:Prop,Actor, Mapper, Property等词。本书不作翻译。
】位置信息的參数依赖于对象是否在渲染场景中,比方一个三维物体或者二维凝视,它们的位置信息控制方式是有所差别的。三维的Prop如vtkActor和vtkVolume(vtkActor和vtkVolume都是vtkProp3D的子类,而vtkProp3D继承自vtkProp),既能够直接控制对象的位置、方向和放缩信息。也能够通过一个4×4的变换矩阵来实现。而对于二维凝视功能的Props如vtkScalarBarActor,其大小和位置有很多的定义方式。当中包含指定相对于视口的位置、宽度和高度。Prop除了提供对象的位置信息控制之外,Prop内部通常还有两个对象。一个是Mapper对象,负责存放数据和渲染信息,还有一个是Property(属性)对象。负责控制颜色、不透明度等參数。
VTK中定义了大量的功能细化的Prop(超过50个),如vtkImageActor(负责图像显示)和vtkPieChartActor(用于创建数组数据的饼图可视表示)。当中的有些Props内部直接包含了控制显示的參数和待渲染数据的索引,因此并不须要额外的Property和Mapper对象。vtkActor的子类vtkFollower能够自己主动的更新方向信息保持自身始终面向一个特定的相机,这样不管怎样旋转渲染场景中的对象,vtkFellower对象都是可见的,适用于三维场景中的广告板(Billboards)或者是文本。
vtkActor的子类vtkLodActor能够自己主动改变自身的几何表示来实现所要求的交互帧率,vtkProp3D的子类vtkLODProp3D则是通过从很多Mapper中进行选择来实现不同的交互性(能够是Volumetric Mapper和GeometricMapper的集合)。vtkAssembly建立了Actor的等级结构以便在整个结构平移、旋转或者放缩时可以更合理的控制变换。
vtkAbsractMapper。很多Props如vtkActor和vtkVolume利用vtkAbstractMapper的子类来保存输入数据的引用以及提供真正的渲染功能。
vtkPolyDataMapper是渲染多边形几何数据基本的Mapper类。而对于体数据,VTK提供了多种渲染技术。比如。vtkFixedPointVolumeRayCastMapper用来渲染vtkImageData类型的数据。vtkProjectedTetrahedraMapper则是用来渲染vtkUnstructuredGrid类型的数据。
vtkProperty和vtkVolumeProperty。某些Props採用单独的属性对象来存储控制数据外观显示的參数,这样不同的对象能够轻松的实现外观參数的共享。vtkActor利用vtkProperty对象存储外观(属性)參数,如颜色、不透明度、材质的环境光(Ambient)系数、散射光(Diffuse)系数和反射光(Specular)系数等。而vtkVolume则是採用vtkVolumeProperty对象来获取体对象的绘制參数。如将标量值映射为颜色和不透明度的传输函数(Transfer Function)【译者:也有译成“传递函数”】。
另外,一些vtkMapper提供对应的函数设置裁剪面以便显示对象的内部结构。
vtkCamera。vtkCamera存储了场景中的摄像机參数。换言之,怎样来“看”渲染场景里的对象。主要參数是摄像机的位置、焦点、和场景中的上方向向量。其它參数能够控制视图变换。如平行投影或者透视投影。图像的尺度或者视角,以及视景体的远近裁剪平面等。
vtkLight。vtkLight对象主要用于场景中的光照计算。vtkLight对象中存储了光源的位置和方向,以及颜色和强度等。另外。还须要一个类型来描写叙述光源相对于摄像机的运动。比如,HeadLight始终位于摄像机处。并照向焦点方向;而SceneLight则始终固定在场景中的某个位置。
vtkRenderer。组成场景的对象包含Prop。Camara和Light都被集中在一个vtkRenderer对象中。vtkRenderer负责管理场景的渲染过程。
一个vtkRenderWindow中能够有多个vtkRenderer对象。而这些vtkRenderer能够渲染在窗体中不同的矩形区域中(视口)。甚至能够是覆盖的区域。
vtkRendererWindow。vtkRendererWindow将操作系统与VTK渲染引擎连接到一起。不同平台下的vtkRendererWindow子类负责本地计算机系统中窗体创建和渲染过程的管理。当使用VTK开发应用程序时,仅仅须要使用平台无关的vtkRendererWindow类,程序执行时,系统会自己主动替换为平台相关的vtkRendererWindow子类。
vtkRendererWindow中包括了vtkRenderer的集合,以及控制渲染的參数,如立体显示(Stereo)、反走样、运动模糊(Motion Blur)和焦点深度(FocalDepth)。
vtkRenderWindowInteractor。vtkRenderWindowInteractor负责监听鼠标、键盘和时钟消息,并通过VTK中的Command/Observer设计模式进行对应的处理。
vtkInteractorStyle监听这些消息并进行处理以完毕旋转、拉伸和放缩等运动控制。vtkRenderWindowInteractor自己主动建立一个默认的3D场景交互器样式(InteractorStyle)。当然你也能够选择一个二维图像浏览的交互器样式。或者是创建自己定义的交互器样式。
vtkTransform。场景中的很多对象,如Prop、光源Light、照相机Camera等都须要在场景中合理的放置,它们通过vtkTransform參数可以方便的控制对象的位置和方向。vtkTransform可以描写叙述三维空间中的线性坐标变换,其内部表示为一个4×4的齐次变换矩阵。
vtkTransform对象初始化为一个单位矩阵,你可以通过管线连接的方式将变换进行组合来完毕复杂的变换。管线方式可以确保当当中任一个变换被改动时,其兴许的变换都会对应的进行更新。
vtkLookupTable,vtkColorTransferFunction和vtkPiecewiseFunction。标量数据可视化常常须要定义一个标量数据到颜色和不透明度的映射。在几何面绘制中用不透明度定义表面的透明程度。而体绘制中不透明度表示光线穿透物体时不透明度沿着光线的累积效果,两者都须要定义不透明度的映射。对于几何渲染能够使用vtkLookupTable来创建映射,体绘制中须要使用vtkColorTransferFunction和vtkPiecewiseFunction来建立映射。
一个简单的样例。以下的样例(摘自./VTK/Examples/Rendering/CXX/Cylinder.cxx)演示了如何利用上述对象来指定和渲染场景。
vtkCylinderSource*cylinder = vtkCylinderSource::New(); vtkPolyDataMapper*cylinderMapper = vtkPolyDataMapper::New(); cylinderMapper->SetInputConnection(cylinder->GetOutputPort()); vtkActor*cylinderActor = vtkActor::New(); cylinderActor->SetMapper(cylinderMapper); vtkRenderer*ren1 = vtkRenderer::New(); ren1->AddActor(cylinderActor); vtkRenderWindow*renWin = vtkRenderWindow::New(); renWin->AddRenderer(ren1); vtkRenderWindowInteractor*iren = vtkRenderWindowInteractor::New(); iren->SetRenderWindow(renWin); renWin->Render(); iren->Start();
样例中我们直接创建了一个vtkActor,vtkPolyDataMapper。vtkRenderer,vtkRendererWindow和vtkRendererWindowInteractor。注意。vtkProperty会由vtkActor自己主动创建,而vtkLight和vtkCamera会由vtkRenderer自己主动创建。