先看下面的模型,这是一个Cow的三维模型,
在使用中,你是否会有下面的操作?
1.将Cow移动到某个位置——平移
2.转动到Cow背面——旋转
3.改变它大小——缩放
等等
可能你会说,这还不简单,通过操作相机就好了。然而并不是这样,操作相机,只使得相机的空间位置发生了变化,对三维物体并没有改变,要想改变模型,就需要对模形本身做空间变换。
空间变换的基础知识
变换矩阵(Transformation Matrices)
我们都知道,在屏幕上显示的是二维的图形,三维图形都是投影到二维平面的,但它们定义在三维空间的。空间变换的基本元素都是三维坐标点,在计算机图形学中,三维坐标点用齐次坐标表示。利用齐次坐标,可以将空间变换用4x4的矩阵来表示。这些变换最终都是由矩阵的运算完成。
VTK有关空间变换的类和方法
VTK相关的类有:
vtkTransform, vtkTransformFilter, vtkMatrix4x4等
相关的方法有:
• RotateX(angle)、RotateY(angle)、RotateZ(angle)
• RotateWXYZ(angle,x,y,z)
• Scale(x,y,z)
• Translate(x,y,z)
• SetMatrix(m)、GetMatrix(m)
• PostMultiply()、PreMultiply()
• SetPosition(x,y,z)、GetPosition(x,y,z)
• SetOrientation(x,y,z)、 GetOrientation(x,y,z)
• SetScale(sx,sy,sz)
• SetOrigin(x,y,z)、GetOrigin
下面通过几个场景来看看上面的一些类和方法是如何使用以及一些常见的问题。
[图1 圆锥体位于(0,0,0),坐标轴向量长度为1个单位]
场景一:矩阵的顺序问题
我们要对图1中位于世界坐标系原点的圆锥体做如下操作:
1. 圆锥体在X轴正方向上移动1个单位
2.绕Z轴旋转
1 vtkSmartPointer<vtkTransform> trans = 2 vtkSmartPointer<vtkTransform>::New(); 3 trans->PostMultiply(); 4 trans->Translate(1, 0, 0); 5 trans->RotateZ(45); 6 coneActor->SetUserTransform(trans);
试着改变一下1, 2两步的顺序:
对比来看,是两个不同的结果。而这个结果正是因为矩阵乘法并不满足交换定律。
因此,在使用时,进行变换的顺序非常重要,将对结果产生直接的影响。
对于变换矩阵,VTK是用以下顺序来应用这些变化的:
1. 移动actor到它的origin,缩放和旋转都是基于这个点完成的。
2. 缩放几何体。
3.actor依次绕Y, X和Z轴旋转。
4.从旋转中心移回到原来的位置后再移动actor到最终的位置。
在VTK中,矩阵乘法默认是左乘,即PreMultiply,上面的公式也遵循这个原则。为了便于理解,示例代码特地使用了右乘PostMultiply.
场景二:GetMatrix和GetUserMatrix的区别
Code1:
1 coneActor->RotateZ(45); 2 coneActor->SetPosition(1, 0, 0);
Code2:
1 trans->RotateZ(45); 2 trans->Translate(1, 0, 0); 3 coneActor->SetUserTransform(trans);
从两个结果可以看出,GetMatix和GetUserMatrix都是获取变换矩阵。所不同的是:
GetMatrix始终都可以获取到值,也就是说它得到的是圆锥体在世界坐标系的变换矩阵。
GetUserMatrix在Code2中获取到了值,而在Code1中得到是NULL,这说明它获取的是用户设置的变换矩阵。
注:GetUserMatrix、SetUserMatrix和GetUserTransform、SetUserTransform具有相同的作用。
完整示例代码
1 #include <vtkLineSource.h> 2 #include <vtkPolyData.h> 3 #include <vtkSmartPointer.h> 4 #include <vtkPolyDataMapper.h> 5 #include <vtkActor.h> 6 #include <vtkRenderWindow.h> 7 #include <vtkRenderer.h> 8 #include <vtkRenderWindowInteractor.h> 9 #include <vtkProperty.h> 10 #include <vtkAxesActor.h> 11 #include <vtkConeSource.h> 12 #include <vtkTextActor.h> 13 #include <vtkTextProperty.h> 14 #include <vtkTransform.h> 15 #include <vtkSphereSource.h> 16 17 int main(int, char *[]) 18 { 19 vtkSmartPointer<vtkSphereSource> sphereSource = 20 vtkSmartPointer<vtkSphereSource>::New(); 21 sphereSource->SetRadius(0.1); 22 sphereSource->Update(); 23 24 vtkSmartPointer<vtkPolyDataMapper> sphereMapper = 25 vtkSmartPointer<vtkPolyDataMapper>::New(); 26 sphereMapper->SetInputConnection(sphereSource->GetOutputPort()); 27 28 vtkSmartPointer<vtkActor> sphereActor = 29 vtkSmartPointer<vtkActor>::New(); 30 sphereActor->SetPosition(0, 0, 0); 31 sphereActor->SetMapper(sphereMapper); 32 sphereActor->GetProperty()->SetColor(1, 0, 0); 33 34 vtkSmartPointer<vtkConeSource> coneSource = 35 vtkSmartPointer<vtkConeSource>::New(); 36 coneSource->SetRadius(.2); 37 coneSource->SetHeight(.5); 38 coneSource->SetCenter(0, 0, 0); 39 vtkSmartPointer<vtkPolyDataMapper> coneMapper = 40 vtkSmartPointer<vtkPolyDataMapper>::New(); 41 coneMapper->SetInputConnection(coneSource->GetOutputPort()); 42 vtkSmartPointer<vtkActor> coneActor = 43 vtkSmartPointer<vtkActor>::New(); 44 coneActor->SetMapper(coneMapper); 45 46 vtkSmartPointer<vtkActor> oriConeActor = 47 vtkSmartPointer<vtkActor>::New(); 48 oriConeActor->SetMapper(coneMapper); 49 #define AXIS_LEN 1. 50 vtkSmartPointer<vtkAxesActor> oriAxesActor = 51 vtkSmartPointer<vtkAxesActor>::New(); 52 oriAxesActor->SetPosition(0, 0, 0); 53 oriAxesActor->SetTotalLength(AXIS_LEN, AXIS_LEN, AXIS_LEN); 54 oriAxesActor->SetShaftType(0); 55 oriAxesActor->SetAxisLabels(0); 56 oriAxesActor->SetCylinderRadius(0.02); 57 58 vtkSmartPointer<vtkAxesActor> axesActor = 59 vtkSmartPointer<vtkAxesActor>::New(); 60 axesActor->SetPosition(0, 0, 0); 61 axesActor->SetTotalLength(AXIS_LEN, AXIS_LEN, AXIS_LEN); 62 axesActor->SetShaftType(0); 63 axesActor->SetAxisLabels(0); 64 axesActor->SetCylinderRadius(0.02); 65 66 vtkSmartPointer<vtkTextActor> textActor = 67 vtkSmartPointer<vtkTextActor>::New(); 68 textActor->SetPosition2(100, 40); 69 textActor->GetTextProperty()->SetFontSize(24); 70 textActor->GetTextProperty()->SetColor(1, 0, 0); 71 72 73 vtkSmartPointer<vtkTransform> trans = 74 vtkSmartPointer<vtkTransform>::New(); 75 76 #if 0 77 trans->PostMultiply(); 78 79 coneActor->SetPosition(1, 0, 0); 80 //trans->Translate(1, 0, 0); 81 //trans->RotateZ(45); 82 trans->RotateZ(45); 83 trans->Translate(1, 0, 0); 84 coneActor->SetUserTransform(trans); 85 //textActor->SetInput("PostMultiply()\nTranslate(1, 0, 0)\nRotateZ(45)"); 86 textActor->SetInput("PostMultiply()\nRotateZ(45)\nTranslate(1, 0, 0)"); 87 #endif 88 89 #if 1 90 //coneActor->RotateZ(45); 91 //coneActor->SetPosition(1, 0, 0); 92 //textActor->SetInput("coneActor->RotateZ(45)\nconeActor->SetPosition(1, 0, 0)"); 93 94 trans->RotateZ(45); 95 trans->Translate(1, 0, 0); 96 coneActor->SetUserTransform(trans); 97 textActor->SetInput("trans->RotateZ(45)\ntrans->Translate(1, 0, 0)\nconeActor->SetUserTransform(trans)"); 98 99 cout << "GetMatrix:" << endl; 100 if (coneActor->GetMatrix()!=NULL) 101 { 102 coneActor->GetMatrix()->Print(cout); 103 } 104 else 105 { 106 cout << "NULL" << endl; 107 } 108 cout << "GetUserMatrix:" << endl; 109 if (coneActor->GetUserMatrix() !=NULL) 110 { 111 coneActor->GetUserMatrix()->Print(cout); 112 } 113 else 114 { 115 cout << "NULL" << endl; 116 } 117 //cout << "GetUserTransform:" << endl; 118 //if (coneActor->GetUserTransform() !=NULL) 119 //{ 120 // coneActor->GetUserTransform()->Print(cout); 121 //} 122 //else 123 //{ 124 // cout << "NULL" << endl; 125 //} 126 #endif 127 vtkSmartPointer<vtkRenderer> renderer1 = 128 vtkSmartPointer<vtkRenderer>::New(); 129 vtkSmartPointer<vtkRenderer> renderer2 = 130 vtkSmartPointer<vtkRenderer>::New(); 131 132 vtkSmartPointer<vtkRenderWindow> renderWindow = 133 vtkSmartPointer<vtkRenderWindow>::New(); 134 renderWindow->SetSize(800, 400); 135 renderWindow->AddRenderer(renderer1); 136 renderWindow->AddRenderer(renderer2); 137 vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 138 vtkSmartPointer<vtkRenderWindowInteractor>::New(); 139 renderWindowInteractor->SetRenderWindow(renderWindow); 140 141 double leftViewport[] = { 0.0, 0.0, 0.5, 1.0 }; 142 double rightViewport[] = { 0.5, 0.0, 1.0, 1.0 }; 143 144 renderer1->AddActor(oriAxesActor); 145 renderer1->AddActor(sphereActor); 146 renderer1->AddActor(oriConeActor); 147 renderer2->AddActor(axesActor); 148 renderer2->AddActor(sphereActor); 149 renderer2->AddActor(coneActor); 150 renderer2->AddActor2D(textActor); 151 renderer1->SetBackground(.3, .3, .5); 152 renderer2->SetBackground(.2, .4, .5); 153 renderer1->SetViewport(leftViewport); 154 renderer2->SetViewport(rightViewport); 155 156 renderWindow->Render(); 157 renderWindowInteractor->Start(); 158 159 return EXIT_SUCCESS; 160 }