1. 图形学中有用的坐标空间
开发人员需要不同的坐标空间,因为某些信息是有意义的或仅在特定上下文环境中可用。
1.1 世界空间
真实的世界采用球面坐标,使用纬度和经度来表示地球上的某一个位置。真实世界中的起点被确定在赤道上,与英格兰格林威治镇的皇家天文台位于同一经度。
世界坐标系(World Coordinate System)是一个特殊的坐标系,它为所有其他指定的坐标系建立了一个“全局”参考系。世界坐标系被我们认为是我们关心的最大坐标系。绝大数情况下,它只是表示了一定的空间,而不是整个世界,因为人们可能仅仅关注了CT检查室内的检查床附近的坐标空间,我们可以立即,因为CT检查不能对整个世界进行扫描,它只要对检查床上的病人扫描而已;
世界坐标空间用于描述绝对位置。在不同病人的同一CT机扫描下,都是位于同一个世界坐标中,这是相对这台CT机的绝对空间。
世界坐标空间也成为是全局(Global)坐标空间或通用(Universal)坐标空间。
1.2 对象空间
对象空间(Object Space)是与特定对象关联的坐标空间。每个对象都有自己独立的对象空间。当对象移动或者改变方向时,与该对象关联的对象坐标空间也会移动或者改变方向。对象空间是一个相对空间,空间是与对象相关,是以对象为中心,与对象外部的空间不相干。手在人体上的位置,不会因为这个人躺着在屋子内,或者这个人站在悬崖边上。我们说的XX物在我的左手边,或者在我前面,这都是对象空间。我们说的东西南北,跟说的前后左右不同,东西南北是世界空间中描述方位的一种方式,是一个绝对位置;前后左右是有一个对象作为参考对象的,是相对位置;
在图形的上下文中,对象空间也被成为是模型(Model)空间,因为模型顶点的坐标以模型空间表示。对象空间也称为体(Body)空间,特别是在物理环境中。
1.3 相机空间
对象空间的一个特例是相机空间(Camera Space),其是与用于渲染的视点相关联的对象空间。
在相机空间中,相机位于原点,+x指向右侧,+z指向前方(指向屏幕内部,相机朝向的方向),+y指向“向上”(是相机的朝上方向)。
相机空间是传统的左手坐标系统约定。OpenGL传统上是右手坐标系统,-z指向屏幕内部,+z从屏幕指向观众;
需要注意是相机空间(三维空间)内的物体坐标,与屏幕空间(二维空间)之间的差异。从相机空间坐标到屏幕空间坐标的映射涉及的操作成为投影(Projection)。
1.4 屏幕空间
屏幕空间是二维空间,起点是(0,0)到(width,height)。屏幕空间内的图像是几何体裁剪到视椎体后,所投影到屏幕空间上的像素图像;
VTK空间坐标体系
计算机图形学里常用的坐标系统主要有4中,分别是Model坐标系统、World坐标系统、View坐标系统和Display坐标系统。使用两种表示坐标点的方式:以屏幕像素值为单位和归一化坐标值(各坐标轴取值范围为[-1,1])。
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),可以使用vtkRenderer::SetViewPort()来设置视口的范围(取值为[0,1]);
在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 | 用户自定义坐标系统。 |
VTK中vtkRenderer的坐标转换
View的范围是从[-1,1],中心点位于(0,0);
借用vtkRenderer的方法,来换算体素在相机拍摄图像中的位置,计算相机中像素(真实像素在相机前裁剪平面的投影坐标)所在的世界位置。
已知相机生成图像的坐标,计算出对应的世界坐标:
ren->ViewportToNormalizedViewport(wp2[0], wp2[1]);
ren->NormalizedViewportToView(wp2[0], wp2[1], wp2[2]);
ren->ViewToWorld(wp2[0], wp2[1], wp2[2]);
// 从显示的坐标到世界坐标转换
vtk_renderer_->SetDisplayPoint(in_point.x, in_point.y, 0.0);
vtk_renderer_->DisplayToWorld();
vtk_renderer_->GetWorldPoint(world_point2);
使用GetWorldPoint计算图像中心的世界坐标,可以看到是相机坐标减去前裁剪平面距离的结果;
double new_point[3] = { 0,0,0 };
ren->SetViewPoint(new_point);
ren->ViewToWorld();
ren->GetWorldPoint(new_point);
// 或
double new_point[3] = { 0,0,0 };
ren->ViewToWorld(new_point[0], new_point[1], new_point[2]);
我试着在之前设定相机视平面方向为Y轴方向,垂直于XZ平面;试着修改渲染窗口的尺寸时,产生的结果;
当前的各个参数:
0x00fcf814 {149.72299999999998, -1025.8604209057046, 218.50000000000000} 是相机坐标;
0x00fcf814 {149.72299999999998, 149.72299999999998, 218.50000000000000} 是相机焦点;
0x00fcf7fc {865.87881669664762, 1568.2732872192901} 是相机的前裁剪平面和后裁剪平面;
渲染后使用SetViewPoint和ViewToWorld和GetWorldPoint计算得到中心点的世界坐标为:
0x00fcf75c {149.72299999999998, -159.98160420905720, 218.49999999999997};该点位于前裁剪平面上;
当前的相机坐标向焦点移动,不能超过焦点;
计算出相机生成图像的世界坐标范围,计算出[-1,1]两端的范围值;
double vp[4] = { 0 };
ren->GetViewport(vp);
double wp1[3] = { vp[0], vp[1], 0.0 };
double wp2[3] = { vp[2], vp[3], 0.0 };
ren->NormalizedDisplayToViewport(wp1[0], wp1[1]);
ren->ViewportToNormalizedDisplay(wp1[0], wp1[1]);
ren->NormalizedViewportToView(wp1[0], wp1[1], wp1[2]);
ren->ViewToWorld(wp1[0], wp1[1], wp1[2]);
ren->NormalizedDisplayToViewport(wp2[0], wp2[1]);
ren->ViewportToNormalizedViewport(wp2[0], wp2[1]);
ren->NormalizedViewportToView(wp2[0], wp2[1], wp2[2]);
ren->ViewToWorld(wp2[0], wp2[1], wp2[2]);
wp1 = 0x0073f7cc {149.72299999999998, -159.98160420905722, 218.50000000000000}
wp2 = 0x0073f7ac {381.73452967708278, -159.98160420905722, 450.51152967708276}
由于焦点和相机的向量方向与Y轴平行,垂直于XZ平面;
所以wp1和wp2的Y轴坐标没有改变;
改变的只有X方向和Y方向;
X方向改变值为(381.73452967708278-149.72299999999998)/1000;
Y方向改变值为(450.51152967708276-218.50000000000000)/600;
长宽为1000*600时:
wp1 = 0x001ff83c {-236.96288279513800, -159.98160420905720, -13.511529677082807}
wp2 = 0x001ff81c {536.40888279513797, -159.98160420905720, 450.51152967708276}
长宽为500*400时:
wp1 = 0x006ffa80 {-140.29141209635350, -159.98160420905722, -13.511529677082809}
wp2 = 0x006ffa60 {439.73741209635352, -159.98160420905722, 450.51152967708282}
长宽为500*500时:
wp1 = 0x00bef7bc {-82.288529677082792, -159.98160420905722, -13.511529677082761}
wp2 = 0x00bef79c {381.73452967708278, -159.98160420905722, 450.51152967708276}
长宽为500*600时:
wp1 = 0x00aff6f4 {-43.619941397569008, -159.98160420905720, -13.511529677082807}
wp2 = 0x00aff6d4 {343.06594139756896, -159.98160420905720, 450.51152967708276}
长宽为500*800时:
wp1 = 0x009cf444 {4.7157939518232537, -159.98160420905722, -13.511529677082809}
wp2 = 0x009cf424 {294.73020604817674, -159.98160420905722, 450.51152967708282}
长宽为500*1000时:
wp1 = 0x00fffa10 {33.717235161458596, -159.98160420905722, -13.511529677082761}
wp2 = 0x00fff9f0 {265.72876483854134, -159.98160420905722, 450.51152967708276}
从上面观察得到,Z轴改变值保持不变;