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对象的方法:

SetScale (double) / GetScale ():设置/获取各向同性的缩放比例。

SetScale (double, double,double) / GetScale (double):设置/获取各同异性的缩放比例。

RotateX (double) / RotateY (double) / RotateZ (double):分别绕X、Y、Z轴旋转指定角度。

RotateWXYZ (double, double,double, double):绕指定的方向旋转指定的角度,第一个参数表示旋转角度,后三个参数确定一个方向。GetOrientationWXYZ()用于获取对应的数据。

SetOrientation (double, double,double) / GetOrientation ():设置vtkProp3D对象的方向。先绕Z轴旋转,然后绕X轴,最后绕Y轴旋转,从而确定vtkProp3D对象的方向。

AddOrientation (double, double,double):在vtkProp3D对象的当前方向增加一个给

定的偏移。

SetPosition (double, double,double) / GetPosition ():在世界坐标系下,指定/获取vtkProp3D对象的位置。

AddPosition (double, double,double):在vtkProp3D对象的当前位置增加一个偏移。

SetOrigin (double, double,double ) / GetOrigin ():设置/获取vtkProp3D对象的原点。

3.2.2vtkActor及其属性

前面的内容已经提到:Prop依赖于两个对象,一个是Mapper(vtkMapper)对象,负责存放数据和渲染信息,另一个是属性(vtkProperty)对象,负责控制颜色、不透明度等参数。对应的这两个方法就是在类vtkActor里定义的:

SetMapper (vtkMapper *):设置定义Actor几何形状的Mapper。

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

================================================

一个稍微复杂的VTK程序相关推荐

  1. Vulkan是什么?和我一起完成一个简单的Vulkan应用程序

    在本章,你将学到: Vulkan以及它背后的基本原理: 如何创建一个最简单的Vulkan应用程序: 在本书其余部分将使用到的术语和概念. 本章将介绍并解释Vulkan是什么.我们会介绍API背后的基本 ...

  2. 一个毕业6年的程序员工作经历和成长感悟(终)

    接上篇:一个毕业6年的程序员工作经历和成长感悟(上) 一个毕业6年的程序员工作经历和成长感悟(中) 一个毕业6年的程序员工作经历和成长感悟(下) 回望过去6年的经历,自己有了很多变化:从表面上来说,工 ...

  3. 一个三流院校毕业的程序员奋斗历程

    这些日子我一直在写一个实时操作系统内核,已有小成了,等写完我会全部公开,希望能够为国内IT的发展尽自己一份微薄的力量.最近看到很多学生朋友和我当年一样没有方向 ,所以把我的经历写出来与大家共勉,希望能 ...

  4. 一个完整的Installshield安装程序实例—艾泽拉斯之海洋女神出品(三) --高级设置一...

    一个完整的Installshield安装程序实例-艾泽拉斯之海洋女神出品(三) --高级设置一 原文:一个完整的Installshield安装程序实例-艾泽拉斯之海洋女神出品(三) --高级设置一 上 ...

  5. 【 C 】简化双链表插入函数(对在双链表中插入一个新值的简明程序的简化)

    目录 背景 第一个技巧是语句提炼(statement factoring) 第二个简化技巧 最终简化版本 背景 上篇博文:[ C ]在双链表中插入一个新值的简明程序,讲了一个简明的双链表插入函数,那个 ...

  6. Windows下编译TensorFlow1.3 C++ library及创建一个简单的TensorFlow C++程序

    参考:https://www.cnblogs.com/jliangqiu2016/p/7642471.html Windows下编译TensorFlow1.3 C++ library及创建一个简单的T ...

  7. python软件代码示例-用Python写一个模拟qq聊天小程序的代码实例

    Python 超简单的聊天程序 客户端: import socket, sys host = '10.248.27.23' # host = raw_input("Plz imput des ...

  8. 安装汇编环境,写一个最简单的窗口程序

    用汇编写一个窗口程序 1.安装汇编所需的环境 1.下载masm32 下载地址 可以安装到D:\masm32目录下 2.配置环境变量 include = D:\masm32\include lib = ...

  9. 为什么3年的Java高级程序员薪水仅仅8k-10k,而一个Linux底层C语言程序员两年经验就敢要1...

    为什么80%的码农都做不了架构师?>>>    为什么3年的Java高级程序员薪水仅仅8k-10k,而一个Linux底层C语言程序员两年经验就敢要10k的薪水?   由于目前国内嵌入 ...

最新文章

  1. Java 中的关键字 final
  2. KVM创建虚拟机(六)
  3. 如何基于K8s构建下一代DevOps平台?
  4. 由铁路订票系统联想到的
  5. 重操JS旧业第十一弹:BOM对象
  6. docker源码_使用docker、Jenkins、gitlee、springboot、搭建个人博客网站 并实现CI/CD 外加机器人提醒...
  7. 【树莓派学习笔记】一、烧录系统、(无屏幕)配置Wifi和SSH服务
  8. 〖Android〗屏幕触屏事件录制与回放
  9. C++STL库中的map容器
  10. 如何elf文件转换为asm汇编文件
  11. JavaScript获取移动设备型号的实现代码(JS获取手机型号和系统)
  12. html英雄联盟网页,Html+Css+JQuery实现简易英雄联盟官网
  13. 13家著名IT公司logo变迁史
  14. 利用ACC控制酷狗音乐
  15. 在电脑双屏使用时,搜狗输入法在别的屏幕(转)
  16. mysql关闭空闲连接_终止空闲的mysql连接
  17. 从源码解析-Android系统启动流程概述 init进程zygote进程SystemServer进程启动流程
  18. 关于负数在计算机中的表示方法
  19. 思迈特Smartbi数据填报功能
  20. 00-前端开发和前端开发工具

热门文章

  1. 关于excel的join怎么做
  2. 当你一无所有的时候,是学习最好的状态
  3. 编写安全的代码(ACL使用方法)
  4. 【转】C# 中的委托和事件
  5. adb shell 命令详解,android, adb logcat
  6. Iptables 入门进阶
  7. 搭建YUM服务器,配置yum客户端
  8. mysql免安装版配置方法
  9. csv文件导入sqlite
  10. 以KNN为例用sklearn进行数据分析和预测