一、坐标系统
- Model坐标系统:定义模型时所采用的坐标系统,通常是局部的笛卡尔坐标系。
- World坐标系统:是放置Actor的三维空间坐标系,Actor(vtkActor类)其中的一个功能就是负责将模型从Model坐标系统变换到World坐标系统。每一个模型可以定义自己的Model坐标系统,但World坐标系只有一个,每一个Actor必须通过放缩、旋转、平移等操作将Model坐标系变换到World坐标系。World坐标系同时也是相机和灯光所在的坐标系统。
- View坐标系统:表示的是相机所看见的坐标系统。X、Y、Z轴取值为[-1, 1],X、Y值表示像平面上的位置,Z值表示到相机的距离。相机负责将World坐标系变换到View坐标系。
- Display坐标系统:与View坐标系统类似,但是各坐标轴的取值不是[-1, 1],而是使用屏幕的像素值。屏幕上显示的不同窗口的大小会影响View坐标系的坐标值[-1, 1]到Display坐标系的映射。可以把不同的渲染场景放在同一个窗口进行显示,例如,在一个窗口里,分为左右两个渲染场景,这左右的渲染场景(vtkRenderer)就是不同的视口(Viewport)。
示例Viewport实现将一个窗口分为4个视口:
#include <QApplication>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkActor.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderer.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkConeSource.h>
#include <vtkCubeSource.h>
#include <vtkCylinderSource.h>
#include <vtkSphereSource.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 创建四个不同形状的源对象
vtkNew<vtkConeSource> cone;
vtkNew<vtkCubeSource> cube;
vtkNew<vtkCylinderSource> cylinder;
vtkNew<vtkSphereSource> sphere;
// 创建四个Mapper对象,将源对象连接到Mapper上
vtkNew<vtkPolyDataMapper> coneMapper;
coneMapper->SetInputConnection(cone->GetOutputPort());
vtkNew<vtkPolyDataMapper> cubeMapper;
cubeMapper->SetInputConnection(cube->GetOutputPort());
vtkNew<vtkPolyDataMapper> cylinderMapper;
cylinderMapper->SetInputConnection(cylinder->GetOutputPort());
vtkNew<vtkPolyDataMapper> sphereMapper;
sphereMapper->SetInputConnection(sphere->GetOutputPort());
// 创建四个Actor对象,并将Mapper连接到Actor上
vtkNew<vtkActor> coneActor;
coneActor->SetMapper(coneMapper);
vtkNew<vtkActor> cubeActor;
cubeActor->SetMapper(cubeMapper);
vtkNew<vtkActor> cylinderActor;
cylinderActor->SetMapper(cylinderMapper);
vtkNew<vtkActor> sphereActor;
sphereActor->SetMapper(sphereMapper);
// 创建四个Renderer对象,并设置每个Renderer的背景颜色和视口
vtkNew<vtkRenderer> renderer1;
renderer1->AddActor(coneActor);
renderer1->SetBackground(1.0,0.0,0.0);
renderer1->SetViewport(0.0,0.0,0.5,0.5);
vtkNew<vtkRenderer> renderer2;
renderer2->AddActor(cubeActor);
renderer2->SetBackground(0.0,1.0,0.0);
renderer2->SetViewport(0.5,0.0,1.0,0.5);
vtkNew<vtkRenderer> renderer3;
renderer3->AddActor(cylinderActor);
renderer3->SetBackground(0.0,0.0,1.0);
renderer3->SetViewport(0.0,0.5,0.5,1.0);
vtkNew<vtkRenderer> renderer4;
renderer4->AddActor(sphereActor);
renderer4->SetBackground(1.0,1.0,0.0);
renderer4->SetViewport(0.5,0.5,1.0,1.0);
// 创建RenderWindow对象,并将四个Renderer添加到RenderWindow中
vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
renderWindow->AddRenderer(renderer1);
renderWindow->AddRenderer(renderer2);
renderWindow->AddRenderer(renderer3);
renderWindow->AddRenderer(renderer4);
QVTKOpenGLNativeWidget w;
w.setRenderWindow(renderWindow);
w.show();
return a.exec();
}
效果如下:
vtkCoordinate可以用来表示坐标系统,其内部提供了函数接口来定义坐标系统:
SetCoordinateSystemToDisplay()
SetCoordinateSystemToNormalizedDisplay()
SetCoordinateSystemToViewport()
SetCoordinateSystemToNormalizedViewport()
SetCoordinateSystemToView()
SetCoordinateSystemToWorld()
另外,该类还实现这些坐标系统之间的转换,例如下述代码实现了归一化窗口坐标与窗口坐标之间的转换:
vtkNew<vtkCoordinate> coordinate;
coordinate->SetCoordinateSystemToNormalizedDisplay();
coordinate->SetValue(.5, .5, 0); // 屏幕中心
int* val = coordinate->GetComputedDisplayValue(renderer); // 窗口坐标转换
二、空间变换
在三维空间里定义的三维模型,最后显示时都是投影到二维平面,比如在屏幕上显示三维到二维的投影包括透视投影(PerspectiveProjection)和正交投影(Orthogonal Projection)。正交投影也叫平行投影。
VTK里与空间变换有关的类有vtkTransform2D,vtkTransform,vtkPerspectiveTransform,vtkGeneralTransform,vtkTransformFilter,vtkMatrix4×4等。例如下面代码实现了vtkActor对象的空间变换:
vtkNew<vtkTransform> transform;
transform->PostMultiply(); // 设置右乘计算变换矩阵。
transform->RotateZ(40); //旋转
transform->Translate(10, 0, 0); // 平移
cylinderActor->SetUserTransform(transform);
三、VTK管线
vtkProp、vtkAbstractMapper、vtkProperty、vtkCamera、vtkLight、vtkRenderer、vtkRendererWindow、vtkRenderWindowInteractor、vtkTransform、vtkLookupTable等等类都是与数据显示或渲染相关的,它们构成了VTK的渲染引擎(Rendering Engine)。渲染引擎主要负责数据的可视化表达,是VTK里的两个重要概念之一,而另一个重要概念就是可视化管线(Visualization Pipeline)。
可视化管线是指用于获取或创建数据、处理数据以及把数据写入文件或者把数据传递给渲染引擎进行显示,这样的一种结构在VTK里就称为可视化管线。数据对象(Data Object)、处理对象(Process Object)和数据流方向(Direction of Data Flow)是可视化管线的三个要素。每个VTK程序都会有可视化管线存在。
Source是指用于创建数据(如vtkCylinderSource)或者读取数据(如vtkBMPReader、vtkStructuredPointsReader等)类的统称,即VTK的数据源。Source输出的数据作为Filter的输入,经Filter处理以后(可以经过多个Filter处理),生成新的数据。Filter的输出可以直接写入文件,或者经Mapper变换后传入渲染引擎进行渲染、显示,结束可视化管线。上图的箭头方向即为VTK里数据流流动的方向。
可视化管线的三要素分别为数据对象、处理对象和数据流方向,Source、Filter和Mapper一起构成了处理对象,它们的区别是基于数据流的初始化、维持和终止。根据数据的生成方式,Source可以分为程序源对象(Procedural,如vtkCylinderSource,通过程序代码生成相关的数据)和读取源对象(Reader,如vtkBMPReader,从外部文件中导入数据)。
四、智能指针
智能指针会自动管理引用计数的增加与减少。
c++中智能指针:
1:unique_ptr :这是一种独占式的智能指针;确保在其使用的生命周期内只有一个指针可以指向被
分配的对象;当其被销毁的时候,其管理的对象也被销毁。
2:share_ptr : 允许多个智能指针共享对同一资源的所有权。它使用引用计数来跟踪资源的引用次数,当最后一个std::shared_ptr离开作用域时,资源会被释放
3:weak_ptr : 允许多个智能指针共享对同一资源的所有权。它使用引用计数来跟踪资源的引用次数,当最后一个std::shared_ptr离开作用域时,资源会被释放。
VTK中智能指针类为vtkSmartPointer。VTKSmartPointer是一个模板类,继承自VTKSmartPointerBase类。VTKSmartPointerBase中定义了一个vtkObjectBase类型的指针对象Object,用于存储智能指针中实际生成的对象。智能指针定义为:
vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New(); //引用计数为1
VTKSmartPointer中定义了静态函数New()来生成一个智能指针对象。该函数的核心在于:会根据模板参数类型来生成一个对象,并将其保存在基类VTKSmartPointerBase的成员变量Object中。
VTKSmartPointer中重载了“->”操作符,返回实际模板参数类型的对象,因此可以方便地访问对象的成员函数,如camera->setFocusPosition(0,0,0)。
VTKSmartPointer重载了“=”操作符,可以在VTKSmartPointer对象之间进行赋值。在赋值过程中,VTKSmartPointer会自动控制其内部对象指针Object的引用计数加1。
例如:
vktSmartPointer<vtkCamera> camera1 =vtkSmartPointer<vtkCamera>::New();
vtkSmartPointer<vtkCamera> camera2 = camera1;
需要注意的是,此时camera1和camera2的引用计数都等于2。
过程为:首先camera1的vtkCamera对象Object调用Register()函数,自动将引用计数加1,然后将camera2的Object指向camera1的Object对象。
当一个智能指针对象的生命周期结束时,会自动调用其析构函数释放内存。在析构函数中会调用其内部对象Object的UnRegister()函数修改引用计数。如果此时的引用计数为0,Object对象会自动释放内存。