3、VTK基础概念
在第2章里,我们已经接触了一个简单的VTK工程,也掌握了怎么使用CMake来构建VTK工程的步骤,本书后续章节的所有例子都是采用第2章介绍的步骤来构建VTK的工程。
本章我们先在第2章TestVTKInstall的基础上作一些更改,演示一个能够交互的VTK应用程序。与前面的风格类似,我们先是让你“知其然”,然后再慢慢地让你“知其所以然”。
3.1 一个稍微复杂的VTK程序
首先当然是写一个CMakeLists.txt文件。在D:\\Toolkits\\VTK\\Examples\\下新建一个文件夹,名为3.1.1_RenderCylinder,在该目录下新建CMakeLists.txt和RenderCylinder.cpp文件。
示例3.1.1_RenderCylinder的CMakeLists.txt |
1: cmake_minimum_required(VERSION 2.8)
2: project(RenderCylinder)
3: find_package(VTK REQUIRED)
4: include(${VTK_USE_FILE})
5: add_executable(${PROJECT_NAME} RenderCylinder.cpp)
6: target_link_libraries(${PROJECT_NAME} ${VTK_LIBRARIES})
示例3.1.1_RenderCylinder |
1: #include "vtkSmartPointer.h"
2: #include "vtkRenderWindow.h"
3: #include "vtkRenderer.h"
4: #include "vtkRenderWindowInteractor.h"
5: #include "vtkInteractorStyleTrackballCamera.h"
6: #include "vtkCylinderSource.h"
7: #include "vtkPolyDataMapper.h"
8: #include "vtkActor.h"
9:
10: int main()
11: {
12: vtkSmartPointer<vtkCylinderSource> cylinder =
13: vtkSmartPointer<vtkCylinderSource>::New();
14: cylinder->SetHeight( 3.0 );
15: cylinder->SetRadius( 1.0 );
16: cylinder->SetResolution( 10 );
17:
18: vtkSmartPointer<vtkPolyDataMapper> cylinderMapper =
19: vtkSmartPointer<vtkPolyDataMapper>::New();
20: cylinderMapper->SetInputConnection( cylinder->GetOutputPort() );
21:
22: vtkSmartPointer<vtkActor> cylinderActor =
23: vtkSmartPointer<vtkActor>::New();
24: cylinderActor->SetMapper( cylinderMapper );
25:
26: vtkSmartPointer<vtkRenderer> renderer =
27: vtkSmartPointer<vtkRenderer>::New();
28: renderer->AddActor( cylinderActor );
29: renderer->SetBackground( 0.1, 0.2, 0.4 );
30:
31: vtkSmartPointer<vtkRenderWindow> renWin =
32: vtkSmartPointer<vtkRenderWindow>::New();
33: renWin->AddRenderer( renderer );
34: renWin->SetSize( 300, 300 );
35:
36: vtkSmartPointer<vtkRenderWindowInteractor> iren =
37: vtkSmartPointer<vtkRenderWindowInteractor>::New();
38: iren->SetRenderWindow(renWin);
39:
40: vtkSmartPointer<vtkInteractorStyleTrackballCamera> style =
41: vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
42: iren->SetInteractorStyle(style);
43:
44: iren->Initialize();
45: iren->Start();
46:
47: return 0;
48: }
跟上一章的步骤一样,运行CMake –> Configure –> Generate。接着打开生成的RenderCylinder.sln文件,F7编译,F5调试运行(这些快捷键可能不同版本的VS不太一样,也可以用菜单来操作,这个步骤在本书后续章节将不再赘述),程序运行结果如图3.1所示。
图3.1 示例3.1.1_RenderCylinder的运行结果
可以使用鼠标与柱体(图3.1)交互,比如用鼠标滚轮可以对柱体放大、缩小;按下鼠标左键不放,然后移动鼠标,可以转动柱体;按下鼠标左键,同时按下Shift键,移动鼠标,可以移动整个柱体,等等。其他的功能你也可以试着摸索一下,比如按下Ctrl键时再按鼠标左键;鼠标停留在柱体上,然后按下P键;试着按一下字母E呢?
接下来我们详细解释一下示例3.1.1_RenderCylinder里每行代码的含义以及所用到的VTK的类。从包含的头文件可以看出,除了vtkSmartPointer和vtkRenderWindow在第2章已经介绍过,其他6个类都是新加入的。
(1) vtkCylinderSource:(第12-16行),派生自vtkPolyDataAlgorithm。顾名思义,vtkCylinderSource生成的数据类型就是PolyData(vtkPolyData)的,它主要是生成一个中心在渲染场景原点的柱体,柱体的长轴沿着Y轴,柱体的高度、截面半径等都可以任意指定。
vtkCylinderSource::SetHeight() ——设置柱体的高。
vtkCylinderSource::SetRadius() ——设置柱体横截面的半径。
vtkCylinderSource::SetResolution() ——设置柱体横截面的等边多边形的边数。转动一下柱体,然后数数柱体横截面有多少条边,应该就能明白这个参数表示什么意思。
(2) vtkPolyDataMapper:(第18-20行),渲染多边形几何数据(vtkPolyData),派生自类vtkMapper,将输入的数据转换为几何图元(点、线、多边形)进行渲染。
vtkPolyDataMapper::SetInputConnection() —— VTK可视化管线的输入数据接口,对应的可视化管线输出数据的接口为GetOutputPort();VTK5.0之前的版本使用SetInput()和GetOutput()作为输入输出接口,VTK 5.0以后版本保留了对这两个接口的支持。关于这两者的区别,后续内容会详细介绍。
(3) vtkActor:(第22-24行),派生自vtkProp类,渲染场景中数据的可视化表达是通过vtkProp的子类负责的。比如,本例要渲染一个柱体,柱体的数据类型是vtkPolyData,数据要在场景中渲染时,不是直接把数据加入渲染场景就可以,待渲染的数据是以vtkProp的形式存在于渲染场景中。三维空间中渲染对象最常用的vtkProp子类是vtkActor(表达场景中的几何数据)和vtkVolume(表达场景中的体数据);二维空间中的数据则是用vtkActor2D表达。vtkProp子类负责确定渲染场景中对象的位置、大小和方向信息。Prop依赖于两个对象(Prop一词来源于戏剧里的“道具”,在VTK里表示的是渲染场景中可以看得到的对象。),一个是Mapper(vtkMapper)对象,负责存放数据和渲染信息,另一个是属性(vtkProperty)对象,负责控制颜色、不透明度等参数。
VTK中定义了大量(超过50个)的Prop类,如vtkImageActor(负责图像显示)和vtkPieChartActor(用于创建数组数据的饼图可视化表达)。其中的一些Prop内部直接包括了控制显示的参数和待渲染数据的索引,因此并不需要额外的Property和Mapper对象。vtkActor的子类vtkFollower可以自动的更新方向信息以保持始终面向一个特定的相机。这样无论怎样旋转,三维场景中的广告板(Billboards)或者文本都是可见的。vtkActor的子类vtkLODActor可以自动改变自身的几何表达来实现需要达到的交互帧率。vtkProp3D的子类vtkLODProp3D则是通过从许多Mapper(可以是体数据的Mapper和几何数据的Mapper集合)中进行选择来实现交互。vtkAssembly建立Actor的等级结构以便在整个结构平移、旋转或者缩放时能够更合理的控制变换。
vtkActor::SetMapper()——设置生成几何图元的Mapper。即连接一个Actor到可视化管线的末端(可视化管线的末端就是Mapper)。
(4) vtkRenderWindow:(第31-34行),将操作系统与VTK渲染引擎连接到一起。不同平台下的vtkRenderWindow子类负责本地计算机系统中窗口创建和渲染过程管理。当使用VTK开发应用程序时,你只需要使用平台无关的vtkRendererWindow类,运行时,系统会自动替换为平台相关的vtkRendererWindow子类。比如,Windows下运行上述的VTK程序,实际创建的是vtkWin32OpenGLRenderWindow(vtkRenderWindow的子类)对象。vtkRenderWindow中包含了vtkRenderer集合、渲染参数,如立体显示(Stereo),反走样,运动模糊(Motion Blur)和焦点深度(Focal Depth)等。
vtkRenderWindow::AddRenderer() ——加入vtkRenderer对象。
vtkRenderWindow::SetSize() ——该方法是从vtkRenderWindow的父类vtkWindow继承过来的,用于设置窗口的大小,以像素为单位。
(5) vtkRenderer:(第26-29行),负责管理场景的渲染过程。组成场景的所有对象包括Prop,照相机(Camera)和光照(Light)都被集中在一个vtkRenderer对象中。一个vtkRenderWindow中可以有多个vtkRenderer对象,而这些vtkRenderer可以渲染在窗口中不同的矩形区域中(即视口),或者覆盖整个窗口区域。
vtkRenderer::AddActor() ——添加vtkProp类型的对象到渲染场景中。
vtkRenderer::SetBackground() ——该方法是从vtkRenderer的父类vtkViewport继承的,用于设置渲染场景的背景颜色,用R、G、B的格式设置,三个分量的取值为0.0~ 1.0。(0.0,0.0, 0.0)为黑色,(1.0,1.0, 1.0)为白色。除了可以设置单一的背景颜色之外,还可以设置渐变的背景颜色,vtkViewport::SetBackground2()用于设置渐变的另外一种颜色,但是要使背景颜色渐变生效或者关闭,必须调用以下的方法:
vtkViewport::SetGradientBackground(bool) ——参数为0是关闭,反之,打开。
vtkViewport::GradientBackgroundOn()—— 打开背景颜色渐变效果,相当于调用方法SetGradientBackground(1)。
vtkViewport::GradientBackgroundOff() ——关闭背景颜色渐变效果。相当于调用方法SetGradientBackground(0)。
(6) vtkRenderWindowInteractor:(第36-38行),提供平台独立的响应鼠标、键盘和时钟事件的交互机制,通过VTK的Command/Observer设计模式将监听到的特定平台的鼠标、键盘和时钟事件交由vtkInteractorObserver或其子类,如vtkInteractorStyle进行处理。vtkInteractorStyle等监听这些消息并进行处理以完成旋转、拉伸和放缩等运动控制。vtkRenderWindowInteractor自动建立一个默认的3D场景交互器样式(Interactor Style):vtkInteractorStyleSwitch,当然你也可以选择其他的交互器样式,或者是创建自己的交互器样式。在本例中,我们就是选择了其他的交互器样式来替代默认的:vtkInteractorStyleTrackballCamera。
vtkRenderWindowInteractor::SetRenderWindow()——设置渲染窗口,消息是通过渲染窗口捕获到的,所以必须要给交互器对象设置渲染窗口。
vtkRenderWindowInteractor::SetInteractorStyle()——定义交互器样式,默认的交互样式为vtkInteractorStyleSwitch。
vtkRenderWindowInteractor::Initialize() ——为处理窗口事件做准备,交互器工作之前必须先调用这个方法进行初始化。
vtkRenderWindowInteractor::Start() ——开始进入事件响应循环,交互器处于等待状态,等待用户交互事件的发生。进入事件响应循环之前必须先调用Initialize()方法。
(7) vtkInteractorStyleTrackballCamera:(第40-41行),交互器样式的一种,该样式下,用户是通过控制相机对物体作旋转、放大、缩小等操作。做个类比:我们在照相的时候如果要想物体看起来显得大一些,我们可以采取两种做法,第一种做法是相机不动,让我们要拍的物体靠近我们;第二种做法是物体不要动,我们把相机靠近物体,这样物体看起来也是大一些。第二种做法就是vtkInteractorStyleTrackballCamera的风格。其父类为vtkInteractorStyle,除了vtkInteractorStyleTrackballCamera之外,VTK还定义了其他多种交互器样式,如vtkInteractorStyleImage,主要用于显示二维图像时的交互。
以上内容我们是逐行代码进行解释,并稍微做了扩展。可能你看了以后会觉得比较零散,还是不够系统、全面。下面我们就从稍微宏观的角度重新看看这个例子的代码。继续下面的内容之前,我们先来看一个类比,可能没有你想的形象,但它对我们理解例子的代码还是会有帮助的。
当我们去看舞台剧的时候,我们坐在台下,展现在我们面前的是一个舞台,舞台上有各式的灯光,各样的演员。演员出场的时候肯定是会先化妆,有些演员可能会打扮成高富帅,有些演员可能会化妆成白富美。观众有时还会与台上的演员有一定的互动。
整个剧院就好比VTK程序的渲染窗口(vtkRenderWindow);舞台就相当于渲染场景(vtkRenderer);而那些高富帅、白富美就是我们程序中的Actor(有些文献翻译成“演员”,有些翻译成“角色”,这里我们不作翻译);台上的演员与台下观众的互动可以看成是程序的交互(vtkRenderWindowInteractor);演员与观众的互动方式有很多种,现场的观众可以直接上台跟演员们握手拥抱,电视机前的可以发短信,电脑、移动终端用户等可以微博关注、加粉等等,这就好比我们程序里的交互器样式(vtkInteractorStyle);舞台上的演员我们都能一一分辨出来,不会把高富帅弄混淆,是因为他们化的妆、穿的服饰都不一样,这就相当于我们程序里vtkActor的不同属性(vtkProperty);台下观众的眼睛可以看作是vtkCamera,前排的观众因为离得近,看台上演员会显得比较高大,而后排的观众看到的会显得小点,每个观众看到的东西在他的世界里都是唯一的,所以渲染场景Renderer里的vtkCamera对象也是只有一个;舞台上的灯光可以有多个,所以渲染场景里的vtkLight也存在多个。可以参考一下图3.2,加深理解。
图3.2VTK渲染场景以及各主要对象
3.2 vtkActor及相关属性
由前面的内容我们知道,数据渲染时不是直接加入渲染场景,而是以vtkProp的形式存在的,因此有必要再详细讨论一下vtkProp及其子类,图3.3为vtkProp类的继承图。
图3.3 类vtkProp的继承图
3.2.1vtkProp和vtkProp3D
vtkProp是任何存在于渲染窗口的对象的父类,包括二维或者三维的对象。换言之,在渲染窗口里能够看得到的对象(这些对象都称作Prop),都是从vtkProp继承过来的。在这个类里定义了用于拾取(Picking)、LOD(Level-Of-Detail)操作的方法,同时也定义了确定Prop是否可见、可否被拾取以及可否被拖动等的变量。即:
SetVisibility(int) / GetVisibility () /VisibilityOn () /VisibilityOff ()
Pick () / SetPickable (int) / GetPickable () / PickableOn() / PickableOff ()
SetDragable (int) / GetDragable () / DragableOn () /DragableOff()
vtkProp3D从vtkProp直接派生,是对象在三维渲染场景中的表达形式。把一个对象放置在三维场景时,首先要考虑的是这个对象到底要放在场景中的哪个位置,摆放的方向如何等等,这些信息在vtkProp3D内部用一个4×4的矩阵来表示。默认构造的vtkProp3D对象原点为(0,0,0),放置的位置为(0,0,0),放置的方向为(0,0,0),用户自定义的矩阵或者变换为空。以下是vtkProp3D定义的用于放置vtkProp3D对象的方法:
l SetScale (double) / GetScale ():设置/获取各向同性的缩放比例。
l SetScale (double, double,double) / GetScale (double):设置/获取各同异性的缩放比例。
l RotateX (double) / RotateY (double) / RotateZ (double):分别绕X、Y、Z轴旋转指定角度。
l RotateWXYZ (double, double,double, double):绕指定的方向旋转指定的角度,第一个参数表示旋转角度,后三个参数确定一个方向。GetOrientationWXYZ()用于获取对应的数据。
l SetOrientation (double, double,double) / GetOrientation ():设置vtkProp3D对象的方向。先绕Z轴旋转,然后绕X轴,最后绕Y轴旋转,从而确定vtkProp3D对象的方向。
l AddOrientation (double, double,double):在vtkProp3D对象的当前方向增加一个给
定的偏移。
l SetPosition (double, double,double) / GetPosition ():在世界坐标系下,指定/获取vtkProp3D对象的位置。
l AddPosition (double, double,double):在vtkProp3D对象的当前位置增加一个偏移。
l SetOrigin (double, double,double ) / GetOrigin ():设置/获取vtkProp3D对象的原点。
3.2.2vtkActor及其属性
前面的内容已经提到:Prop依赖于两个对象,一个是Mapper(vtkMapper)对象,负责存放数据和渲染信息,另一个是属性(vtkProperty)对象,负责控制颜色、不透明度等参数。对应的这两个方法就是在类vtkActor里定义的:
l SetMapper (vtkMapper *):设置定义Actor几何形状的Mapper。
l SetProperty (vtkProperty *):设置Actor的属性,包括表面属性,如环境光(Ambient)、散射光(Diffuse)、镜面光(Specular)、颜色、透明度等;纹理映射;点的大小,线的宽度设置等。每一个Actor都有一个属性对象(vtkProperty的实例)与之相关联,如果没有给Acttor指定相应的属性(3.1.1的例子就没有指定),VTK会指定默认的属性对象。多个Actor可以共享一个属性对象。请看以下的代码:
//控制Actor属性方法一:先获取Actor的Property对象,然后设置对应的参数。
//调用以下方法时,应该先包括头文件:#include“vtkProperty.h”
//改变cylinderActor颜色为红色,可以直接调用SetColor(),也可调用SetDiffuseColor()。
cylinderActor->GetProperty()->SetColor(1.0,0.0,0.0);
//cylinderActor->GetProperty()->SetDiffuseColor(1.0,0.0,0.0);
//控制Actor属性的方法二:先实例化vtkProperty对象,然后加入到Actor里。
vtkSmartPointer<vtkProperty>cylinderProperty = vtkSmartPointer<vtkProperty>::New();
cylinderProperty->SetColor(1.0,0.0,0.0);
cylinderActor->SetProperty(cylinderProperty);
以上两种方法都可以改变一个Actor的属性,第一种方法相对来说比较简单,直接设置即可,第二种方法则需要先实例化一个vtkProperty对象,但第二种方法比较灵活,而且如果想把多个Actor设置成同一属性时,所需的代码是最少的。
接着,我们看看怎么使用vtkProperty给Actor贴纹理图(与纹理图相关的类:vtkTexture),我们在以上例子的基础上作一下修改,在柱体上贴一个图。
//在3.1.1_RenderCylinder程序的最开始加入下列的代码
vtkSmartPointer<vtkBMPReader>bmpReader = vtkSmartPointer<vtkBMPReader>::New();
bmpReader->SetFileName("doling.bmp");
vtkSmartPointer<vtkTexture>texture = vtkSmartPointer<vtkTexture>::New();
texture->SetInputConnection(bmpReader->GetOutputPort());
texture->InterpolateOn();
//实例化cylinderActor对象以后,调用以下代码
cylinderActor->SetTexture(texture);
程序运行结果如图3.4所示:
图3.4 使用vtkProperty和vtkTexture给Actor贴纹理图
==========欢迎转载,转载时请保留该声明信息==========
版权归@东灵工作室所有,更多信息请访问东灵工作室
教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686
================================================
暂无评论内容