《VTK图形图像开发进阶》第2章——坐标系统及空间变换

2.1 坐标系统

计算机图形学里常用的坐标系统主要有4种,分别是Model坐标系统、World坐标系统、View坐标系统和Display坐标系统,此外还有两种表示坐标点的方式:以屏幕像素值为单位和归一化坐标值(各坐标轴取值范围为[-1, 1])。它们之间的关系如下图。

image

  • 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)。

示例2.3_Viewport实现将一个窗口分为4个视口,用vtkRenderer::SetViewport()来设置视口的范围(取值为[0, 1]):

renderer1->SetViewport(0.0, 0.0, 0.5, 0.5);
renderer1->SetViewport(0.5, 0.0, 1.0, 0.5);
renderer1->SetViewport(0.0, 0.5, 0.5, 1.0);
renderer1->SetViewport(0.5, 0.5, 1.0, 1.0);

示例2.3 _Viewport

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

#include <vtkConeSource.h>
#include <vtkCubeSource.h>
#include <vtkCylinderSource.h>
#include <vtkSphereSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkActor.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>

int main()
{
	// 创建四个不同形状的源对象
	vtkSmartPointer<vtkConeSource> cone = vtkSmartPointer<vtkConeSource>::New();
	vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
	vtkSmartPointer<vtkCylinderSource> cylinder = vtkSmartPointer<vtkCylinderSource>::New();
	vtkSmartPointer<vtkSphereSource> sphere = vtkSmartPointer<vtkSphereSource>::New();

	// 创建四个Mapper对象,将源对象连接到Mapper上
	vtkSmartPointer<vtkPolyDataMapper> coneMapper = 	vtkSmartPointer<vtkPolyDataMapper>::New();
	coneMapper->SetInputConnection(cone->GetOutputPort());
	vtkSmartPointer<vtkPolyDataMapper> cubeMapper = 	vtkSmartPointer<vtkPolyDataMapper>::New();
	cubeMapper->SetInputConnection(cube->GetOutputPort());
	vtkSmartPointer<vtkPolyDataMapper> cylinderMapper = 	vtkSmartPointer<vtkPolyDataMapper>::New();
	cylinderMapper->SetInputConnection(cylinder->GetOutputPort());
	vtkSmartPointer<vtkPolyDataMapper> sphereMapper = 	vtkSmartPointer<vtkPolyDataMapper>::New();
	sphereMapper->SetInputConnection(sphere->GetOutputPort());

	// 创建四个Actor对象,并将Mapper连接到Actor上
	vtkSmartPointer<vtkActor> coneActor = vtkSmartPointer<vtkActor>::New();
	coneActor->SetMapper(coneMapper);
	vtkSmartPointer<vtkActor> cubeActor = vtkSmartPointer<vtkActor>::New();
	cubeActor->SetMapper(cubeMapper);
	vtkSmartPointer<vtkActor> cylinderActor = vtkSmartPointer<vtkActor>::New();
	cylinderActor->SetMapper(cylinderMapper);
	vtkSmartPointer<vtkActor> sphereActor = vtkSmartPointer<vtkActor>::New();
	sphereActor->SetMapper(sphereMapper);

	// 创建四个Renderer对象,并设置每个Renderer的背景颜色和视口
	vtkSmartPointer<vtkRenderer> renderer1 = vtkSmartPointer<vtkRenderer>::New();
	renderer1->AddActor(coneActor);
	renderer1->SetBackground(1.0,0.0,0.0);
	renderer1->SetViewport(0.0,0.0,0.5,0.5);
	vtkSmartPointer<vtkRenderer> renderer2 = vtkSmartPointer<vtkRenderer>::New();
	renderer2->AddActor(cubeActor);
	renderer2->SetBackground(0.0,1.0,0.0);
	renderer2->SetViewport(0.5,0.0,1.0,0.5);
	vtkSmartPointer<vtkRenderer> renderer3 = vtkSmartPointer<vtkRenderer>::New();
	renderer3->AddActor(cylinderActor);
	renderer3->SetBackground(0.0,0.0,1.0);
	renderer3->SetViewport(0.0,0.5,0.5,1.0);
	vtkSmartPointer<vtkRenderer> renderer4 = vtkSmartPointer<vtkRenderer>::New();
	renderer4->AddActor(sphereActor);
	renderer4->SetBackground(1.0,1.0,0.0);
	renderer4->SetViewport(0.5,0.5,1.0,1.0);

	// 创建RenderWindow对象,并将四个Renderer添加到RenderWindow中
	vtkSmartPointer<vtkRenderWindow> renWin=vtkSmartPointer<vtkRenderWindow>::New();
	renWin->AddRenderer(renderer1);
	renWin->AddRenderer(renderer2);
	renWin->AddRenderer(renderer3);
	renWin->AddRenderer(renderer4);
	renWin->SetSize( 640, 480 );
	renWin->Render();
	renWin->SetWindowName("Viewport");

	// 创建RenderWindowInteractor对象,并将其与RenderWindow连接
	vtkSmartPointer<vtkRenderWindowInteractor> interactor =
		vtkSmartPointer<vtkRenderWindowInteractor>::New();
	interactor->SetRenderWindow(renWin);

	// 初始化RenderWindow和RenderWindowInteractor,并启动交互循环
	renWin->Render();
	interactor->Initialize();
	interactor->Start();

	return EXIT_SUCCESS;
}

image

在VTK里,Model坐标系统用得比较少,其他三种坐标系统经常使用。他们之间的变换则是由类vtkCoordinate进行管理。根据坐标值的单位、取值范围等不同,可以将坐标系分为以下几类:

  • DISPLAY:X、Y轴的坐标取值为渲染窗口的像素值。坐标原点位于渲染窗口的左下角,这个对于VTK里面的所有二维坐标系统都是一样的,且VTK里的坐标系统都是采用右手坐标系。
  • NORMALIZED DISPLAY:X、Y轴坐标取值范围为[0, 1],跟DISPLAY一样,也是定义在渲染窗口里的。
  • VIEWPORT:X、Y的坐标值定义在视口或者渲染器(Renderer)里。
  • NORMALIZED VIEWPORT:X、Y坐标值定义在视口或渲染器里,取值范围为[0, 1]。
  • VIEW:X、Y、Z坐标值定义在相机所在的坐标系统里,取值范围为[-1, 1],Z值表示深度信息。
  • WORLD:X、Y、Z坐标值定义在世界坐标系统。
  • USERDEFINED:用户自定义坐标系统。

vtkCoordinate可以用来表示坐标系统,其内部提供了函数接口来定义坐标系统:

SetCoordinateSystemToDisplay()
SetCoordinateSystemToNormalizedDisplay()
SetCoordinateSystemToViewport()
SetCoordinateSystemToNormalizedViewport()
SetCoordinateSystemToView()
SetCoordinateSystemToWorld()

另外,该类还实现这些坐标系统之间的转换,例如下述代码实现了归一化窗口坐标与窗口坐标之间的转换:

vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
coordinate->SetCoordinateSystemToNormalizedDisplay();
coordinate->SetValue(.5, .5, 0);	// 屏幕中心

int* val;
val = coordinate->GetComputedDisplayValue(renderer);	// 窗口坐标转换

2.2 空间变换

image

VTK里与空间变换有关的类有vtkTransform2D,vtkTransform,vtkPerspectiveTransform,vtkGeneralTransform,vtkTransformFilter,vtkMatrix4×4等。例如下面代码实现了vtkActor对象的空间变换:

vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
transform->PostMultiply();	// 设置右乘计算变换矩阵。
transform->RotateZ(40);	//旋转
transform->Translate(10, 0, 0);	// 平移
cylinderActor->SetUserTransform(transform);
posted @ 2023-08-07 11:58  sdyan404  阅读(603)  评论(0编辑  收藏  举报