《VTK图形图像开发进阶》第2章——VTK管线

发布时间 2023-08-07 15:24:00作者: sdyan404

2.1 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程序都会有可视化管线存在,如示例2.1_RenderCylinder,其可视化管线如下图:

image

示例2.4_vtkPipelineDemo.cpp

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);

#include <vtkSmartPointer.h>
#include <vtkStructuredPointsReader.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkMarchingCubes.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>

//测试文件:data/head.vtk
int main(int argc, char* argv[])
{
	/*if (argc < 2)
	{
		std::cout<<argv[0]<<" "<<"VTK-File(*.vtk)"<<std::endl;
		return EXIT_FAILURE;
	}*/
	//读入Structured_Points类型的vtk文件。
	vtkSmartPointer<vtkStructuredPointsReader> reader = 
		vtkSmartPointer<vtkStructuredPointsReader>::New();
	//reader->SetFileName(argv[1]);
	reader->SetFileName("data/head.vtk");

	//用移动立方体法提取等值面。
	vtkSmartPointer<vtkMarchingCubes> marchingCubes = 
		vtkSmartPointer<vtkMarchingCubes>::New();
	marchingCubes->SetInputConnection(reader->GetOutputPort());
	marchingCubes->SetValue(0,500);

	//将生成的等值面数据进行Mapper
	vtkSmartPointer<vtkPolyDataMapper> mapper = 
		vtkSmartPointer<vtkPolyDataMapper>::New();
	mapper->SetInputConnection(marchingCubes->GetOutputPort());

	//把Mapper的输出送入渲染引擎进行显示
	//////////////////////////////////////渲染引擎部分////////////////////////////////////
	vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
	actor->SetMapper(mapper);

	vtkSmartPointer<vtkRenderer> renderer = 
		vtkSmartPointer<vtkRenderer>::New();
	renderer->AddActor(actor);
	renderer->SetBackground(1.0, 1.0, 1.0);

	vtkSmartPointer<vtkRenderWindow> renWin = 
		vtkSmartPointer<vtkRenderWindow>::New();
	renWin->AddRenderer(renderer);
	renWin->SetSize( 640, 480 );
	renWin->Render();
	renWin->SetWindowName("vtkPipelineDemo");

	vtkSmartPointer<vtkRenderWindowInteractor> interactor = 
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	interactor->SetRenderWindow(renWin);

	interactor->Initialize();
	interactor->Start();
	//////////////////////////////////////////////////////////////////////////////////////////////////

	return EXIT_SUCCESS;
}

image

image

2.2 VTK可视化管线

VTK中把与类似vtkMarchingCubes对数据做处理的类称为Filter。

image

Source是指用于创建数据(如vtkCylinderSource)或者读取数据(如vtkBMPReader、vtkStructuredPointsReader等)类的统称,即VTK的数据源。Source输出的数据作为Filter的输入,经Filter处理以后(可以经过多个Filter处理),生成新的数据。Filter的输出可以直接写入文件,或者经Mapper变换后传入渲染引擎进行渲染、显示,结束可视化管线。上图的箭头方向即为VTK里数据流流动的方向。

可视化管线的三要素分别为数据对象、处理对象和数据流方向,Source、Filter和Mapper一起构成了处理对象,它们的区别是基于数据流的初始化、维持和终止。根据数据的生成方式,Source可以分为程序源对象(Procedural,如vtkCylinderSource,通过程序代码生成相关的数据)和读取源对象(Reader,如vtkBMPReader,从外部文件中导入数据)。

关于Source、Filter和Mapper的区别,如下图所示。Source没有输入,但至少有一个输出;Filter可以有一个或多个输入,产生一个或多个输出;Mapper接受一个或多个输入,但没有输出,写文件的Writer(如vtkBMPWriter)可以看作Mapper,负责把数据写入文件或者流(Stream)中,因此Mapper是可视化管线的终点,同时也是可视化管线和渲染引擎的桥梁。

image

2.2.1 可视化管线的连接

marchingCubes->SetInputConnection(reader->GetOutputPort());	// 要求连接的两部分之间的数据类型必须一致

上行代码将reader的输出端口与marchingCubes的输入端口建立连接。vtkMarchingCubes作为Filter只接受一个输入,Filter概括起来有以下三种类型

  • 单个输入,产生单个输出;
  • 多个输入,产生单个输出,但输出的数据可有多种用途,比如,读取数据后,可以对其作等值面提取,另外还可以针对读入的数据生成轮廓线(Outline);
  • 单个输入,产生多个输出;

image

2.2.2 可视化管线的执行

可视化管线连接完成后,必须有一种机制来控制管线的执行。

image

如上图所示,假如Filter D的输入发生了变换,E和F是依赖于D的输入的,所以虚线框内的部分是需要重新执行的管线,而C和G是另外一个分支,D输入的改变不影响C和G,所以为了节省运行时间,C和G是不需要重新执行的。

VTK采用一种叫做“惰性赋值”(Lazy Evaluation)的方案来控制管线的执行,惰性赋值是指根据每个对象的内部修改时间来决定什么时候执行管线,只有当用户或者程序发出“请求数据”时,管线才会被执行。vtkObject类里有一个重要的成员变量MTime,管线里的每个从vtkObject派生的类的对象都会跟踪自己的内部修改时间,当遇到“请求数据”时,该对象会比较这个修改时间,如果发现修改时间发生了改变,对象就会执行。换言之,VTK是采用命令驱动(Demand Driven)的方法来控制管线的执行,这种方法的好处是,当对数据对象作了更改时,不必立即做计算,只有当发出请求时,才开始处理,这样能最小化计算所需的时间,以便更流畅地与数据进行交互。

示例2.4_vtkPipelineExecute

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);

#include <vtkSmartPointer.h>
#include <vtkBMPReader.h>
#include <vtkImageData.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkImageViewer2.h>
#include <vtkRenderWindow.h>

//测试文件:data/VTK-logo.bmp
int main(int argc, char* argv[])
{
	/*if (argc < 2)
	{
		std::cout<<argv[0]<<" "<<"BMP-File(*.bmp)"<<std::endl;
		return EXIT_FAILURE;
	}*/
	vtkSmartPointer<vtkBMPReader> reader = vtkSmartPointer<vtkBMPReader>::New();
	std::cout<<"Modification Time of reader (After New()): "<<reader->GetMTime()<<std::endl;
	//reader->SetFileName(argv[1]);
	reader->SetFileName("data/VTK-logo.bmp");
	std::cout<<"Modification Time of reader (After SetFileName()): "<<reader->GetMTime()<<std::endl;

	vtkImageData* imageData = reader->GetOutput();
	std::cout<<"Modification Time of reader (After GetOutput()): "<<reader->GetMTime()<<std::endl;
	reader->Update();
	std::cout<<"Modification Time of reader (After Update()): "<<reader->GetMTime()<<std::endl;

	int extent[6];
	imageData->GetExtent(extent);
	std::cout<<"Extent of image: "<<extent[0]<<" "
		<<extent[1]<<" "<<extent[2]<<" "<<extent[3]<<" "
		<<extent[4]<<" "<<extent[5]<<" "<<std::endl;

	vtkSmartPointer<vtkImageViewer2> viewer = 
		vtkSmartPointer<vtkImageViewer2>::New();
	vtkSmartPointer<vtkRenderWindowInteractor> interactor = 
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	viewer->SetupInteractor(interactor);
	viewer->SetInputData(imageData);
	viewer->Render();

	viewer->SetSize(640, 480);
	viewer->GetRenderWindow()->SetWindowName("vtkPipelineExecute");

	interactor->Initialize();
	interactor->Start();

	return EXIT_SUCCESS;
}

image

image