24.2.4 渲染子系统
乍看VTK拥有一个简洁的面向对象的渲染模型,这个模型由对应于构建三维场景的组件的类组成。例如:vtkActor
是由与vtkCamera
结合在一起的vtkRenderer
来渲染的对象,一个vtkRenderWindow
中可能具有多个vtkRenderer
。该场景由一个或多个vtkLight
提供光照。每个vtkActor
的位置由vtkTransform
控制,而该演员的外观则通过vtkProperty
来制订。最后,该演员的几何表示由vtkMapper
来定义。映射在VTK中扮演着重要的角色,它们用于数据处理管线的结尾,同时向渲染系统提供接口。考虑下面的例子,我们在这个例子中大幅削减数据,然后将结果写入一个文件,最后通过映射将其可视化,并实现与该结果的交互。
vtkOBJReader *reader = vtkOBJReader::New(); reader->SetFileName("exampleFile.obj"); vtkTriangleFilter *tri = vtkTriangleFilter::New(); tri->SetInputConnection(reader->GetOutputPort()); vtkQuadricDecimation *deci = vtkQuadricDecimation::New(); deci->SetInputConnection(tri->GetOutputPort()); deci->SetTargetReduction( 0.75 ); vtkPolyDataMapper *mapper = vtkPolyDataMapper::New(); mapper->SetInputConnection(deci->GetOutputPort()); vtkActor *actor = vtkActor::New(); actor->SetMapper(mapper); vtkRenderer *renderer = vtkRenderer::New(); renderer->AddActor(actor); vtkRenderWindow *renWin = vtkRenderWindow::New(); renWin->AddRenderer(renderer); vtkRenderWindowInteractor *interactor = vtkRenderWindowInteractor::New(); interactor->SetRenderWindow(renWin); renWin->Render();
这里只有一个演员,渲染器 和 渲染窗与映射一同创建,该映射将管线与渲染系统连接起来。也要注意vtkRenderWindowInteractor
的引入,其实例捕捉鼠标与键盘事件,并将之转换为摄像机操作以及其它动作。这一转换过程由vtkInteractorStyle
进行定义(下文还会就此详述)。缺省情况下,许多实例和数据值被置于场景之后。例如:创建了恒等变换,以及一个单独的光源和性质。
随着时间的推进,该对象模型变得更加复杂。多数复杂性来源于开发派生类,这些派生类用于具体指定渲染过程中的某一方面。vtkActor
目前是vtkProp
的特例(prop正如戏台上的道具),而且还有一群这样的道具用于渲染二维的图形叠加和文本,指定三维物体,甚至用于支持诸如体绘制或GPU实现等的高级渲染技术(见图24.4)。
类似地,随着VTK支持的数据模型的增长,各式用于实现数据于渲染系统接口的映射也随之出现。另外一个发生显著扩展的领域是变换的层次结构。这里最初只有一个简易的4×4变换矩阵,现在变成了一个强悍的类层次结构,它们支持包括薄板样条变换(thin-plate spline transformation)在内的非线性变换。例如:原始的vtkPolyDataMapper
拥有支持具体设备的子类(如:vtkOpenGLPolyDataMapper
)。近几年,它已经被图24.4所展示的一种称为“painter”的复杂的图形学管线所取代。
图24.4:显示功能类
painter的设计支持一大类渲染数据的技术,这些技术能够组合起来以提供特殊的渲染效果。这种能力远远超出了于1994年最初实现的简易的vtkPolyDataMapper
。
可视化系统的另外一个重要方面是子系统的选择。在VTK中,有一个类层次picker,被粗略地分成两类对象:一类对象根据与硬件相关方法和软件方法作比对来选择vtkProp
(例如:ray-casting);另一类对象在一次picker运算之后,提供不同水平的信息。例如:一些picker仅提供XYZ世界空间的位置,而不指明它们选择了哪个vtkProp
;其它picker不但给出所选的vtkProp
,还给出组成用于定义道具几何特征网格的具体的点或单元。
24.2.4 事件与交互
与数据交互是可视化的关键一环。在VTK中,交互的方式有多种。最简单的方式是,用户通过命令观察事件并做出合适的反应(命令模式/观察者模式)。vtkObject
的所有子类都保有一列观察者,这些观察者将其自身寄存于对象中。寄存过程中,观察者指出其感兴趣的特殊事件,并加入关联的命令,此命令将在事件发生时被调用。为了说明这一工作原理,来考虑下面的例子,该例中有一个带有观察者的滤波器(本例中是一个多边形削减滤波器),此观察者观察三种事件:StartEvent
,ProgressEvent
,和EndEvent
。这些事件在这三种情况下被该滤波器所调用:滤波器开始执行时,滤波器执行过程中(周期性调用),以及滤波器执行结束时。下面的代码中,vtkCommand
类拥有一个Execute
方法,该方法用于打印与该类执行算法所花费时间有关的恰当信息。
class vtkProgressCommand : vtk Command { public: static vtkProgressCommand *New() { return new vtkProgressCommand; } virtual void Execute(vtkObject *caller, unsigned long, void *callData) { double progress = *(static_cast<double*>(callData)); std::cout << "Progress at " << progress << std::endl; } }; vtkCommand* pobserver = vtkProgressCommand::New(); vtkDecimatePro *deci = vtkDecimatePro::New(); deci->SetInputConnection( byu->GetOutputPort() ); deci->SetTargetReduction( 0.75 ); deci->AddObserver( vtkCommand::ProgressEvent, pobserver );
尽管这是交互的一种原始形式,它也是许多使用VTK的应用程序的基本要素。例如:上述的简短代码可以很容易地转换、用于显示并管理图形界面中的进度条。这一命令/观察者子系统也是VTK中三维挂件的核心,这些挂件是用于数据的请求、操纵以及编辑的复杂的交互性对象,下文将予以描述。
提到上面的例子,很重要的一点是,VTK中的事件都是预定义的,但是这里也为自定义事件开了后门。vtkCommand
类定义了一组枚举型事件(例如:上面例子中的vtkCommand::ProgressEvent
)以及一个用户事件。UserEvent
只是一个整形数值,一般用作一组应用程序中自定义事件的起始抵消值。于是,vtkCommand::UserEvent+100
可能是指一个VTK预定义的事件之外的某个事件。
从用户的角度来看,一个VTK挂件可以看作是场景中的一个演员,只是用户可以通过操纵句柄或者其它几何特性(句柄操纵与几何特性操纵均是基于前文所述之抓取功能——原文:picking functionality,即24.2.4一节中最后一段所述——的)来与之交互。与挂件的交互是很直观的:用户抓住球面句柄并将其移动,或者抓住一条直线并将其移动。然而,在场景的背后,事件被发送出去(例如:InteractionEvent
),而一个编写合理的应用程序就能够观察到这些事件,并采取恰当的行动。例如,它们通常由下面所给出的vtkCommand::InteractorEvent
所触发:
vtkLW2Callback *myCallback = vtkLW2Callback::New(); myCallback->PolyData = seeds; // streamlines seed points, updated on interaction myCallback->Actor = streamline; // streamline actor, made visible on interaction vtkLineWidget2 *lineWidget = vtkLineWidget::New(); lineWidget->SetInteractor(iren); lineWidget->SetRepresentation(rep); lineWidget->AddObserver(vtkCommand::InteractionEvent, myCallback);
实际上,VTK挂件由两个对象构建而成:一个是vtkInteractorObserver
的子类,另一个是vtkProp
的子类。vtkInteractorObserver
只是观察渲染窗中的用户交互(例如:鼠标事件和键盘事件)并处理之。这些操纵通常由突出显示句柄,改变鼠标指针的外观,以及变换数据等所组成,它们都会修改vtkProp
的几何特征。当然,这些挂件的特殊细节要求编写子类来控制其行为的细微差别,目前系统中拥有50多个不同的挂件。
24.2.4 库的总结
VTK是一个大型软件工具箱。目前,系统由大约1500万行代码(包括注释,但是不包括自动生成的包裹层软件),约1000个C++类组成。为了管理系统的复杂度并减少构建和链接的时间,系统被分割放置在十几个子路径中。表24.1列出了这些子路径,并简要总结了这些库所提供的功能。
Common |
VTK核心类 |
Filtering |
用于管理管线数据流的类 |
Rendering |
渲染,抓取,查看图像,以及交互 |
VolumeRendering |
体绘制技术 |
Graphics |
三维几何处理 |
GenericFiltering |
非线性三维几何处理 |
Imaging |
图像处理管线 |
Hybrid |
同时要求使用图形学和图像处理功能的类 |
Widgets |
复杂的交互 |
IO |
VTK的输入和输出 |
Infovis |
信息可视化 |
Parallel |
并行处理(控制器和通信器) |
Wrapping |
对Tcl,Python以及Java的包裹的支持 |
Examples |
内容广泛、文档良好的示例 |
表24.1 VTK的子路径
24.3 回顾与展望
VTK一直是一个非常成功的系统。虽然第一行代码于1993年写出,但是目前,VTK仍然在不断成长壮大、其开发速度也在不断加快2。本节,我们将谈谈一些经验和将来的挑战。
24.3.1 成长管理
VTK发展历程中,最令人惊叹的方面之一就是项目的寿命。开发的速度归因于若干主要原因:
虽然成长是令人兴奋的,确证软件系统的建立,预测VTK的未来,但妥善的管理却是极其困难的。因此,近期VTK将更多地专注于管理社区以及软件的成长。为此,已经采取了若干措施。
首先,创立了正式的管理架构。创建了架构审查委员会(Architectural Review Board),来指导社区和技术的发展,专注于高层次的、战略性的议题。VTK社区也正在组建一个由意见领袖组成的公认的团队,来指导某些VTK子系统的技术开发。
其次,制定了关于更进一步使工具箱模块化的计划,尤其是应对由git引入的工作流功能,还认识到使用者和开发者一般都想在工作中使用工具箱中小的子系统,并且不想构建并链接整个包。此外,为了支持不断成长的社区,对新的功能和子系统的支持是很重要的,即使它们并不一定是工具箱的核心部分。通过创建松散的、模块化的一群模块,在维持核心的稳定性的同时,适应外围的大量代码贡献是可能的。
24.3.2 技术整合
除了软件过程之外,在开发管线当中还有许多技术创新。
24.3.3 开放科学
最后,Kitware和更加广泛的VTK社区决定加入Open Science。从务实的角度讲,它一个这样的方式,我们将传播公开的数据、公开的发表、以及公开的源代码——这是确保我们正在创建可重现的科学系统所必需的特征。虽然VTK一直以来都以开源和公开数据的系统的形式传播,但是文档过程却一直缺乏。在拥有正式书籍[Kit10,SML06]的同时,还一直有各种非正式的方法来收集包括新的源码在内的技术发表物。我们正在通过开发像是VTK Journal3的新的发表机制来改善这种状况,该期刊可以发表由文档、源代码、数据、以及有效的测试图像组成的文章。它还实现了自动化的代码审查(利用VTK的高质量的软件测试过程)以及人对递交文章的审查。
24.3.4 经验教训
虽然VTK很成功,但是还有许多事情我们没有处理好:
vtkImageClass
,它内部处理像素数据组成的数组。然而,在某些情况下,我们不得不将之重构为小的片段,并继续这一过程。一个基本的例子就是数据处理管线。最初,数据管线是通过数据和算法对象的交互而隐式实现的。我们最终认识到我们得创建一种显式的管线执行对象来协调数据与算法之间的交互,并且用于实现不同的数据处理策略。像VTK这样的开源系统的好处之一是许多这些错误能够并且将会随着时间而得以纠正。我们拥有一个积极的、有能力的开发社区,他们每天都在改进着这个系统,并且我们希望在可预见的将来,这一状态能够维持下去。
脚注
1. http://en.wikipedia.org/wiki/Opaque_pointer.
2. See the latest VTK code analysis at http://www.ohloh.net/p/vtk/analyses/latest.
3. http://www.midasjournal.org/?journal=35.
本文链接:http://www.ituring.com.cn/article/6695