1.从图形着色说起
前一个实验显示结果中的图像是白色的,而图形颜色与vtkPolyData属性数据息息相关。由于并未指定任何颜色和属性数据,因此在显示时默认以白色显示。属性数据包括点属性和单元属性。可以为vtkPolyData的点数据和单元数据分别指定属性数据。属性数据可以是标量,如点的曲率;可以是向量,如点或者单元的法向量;也可以是张量,主要在流场中较为常见。颜色可以直接作为一种标量属性数据,设置到相应的点或者单元数据中,这也是最直接的一种图形着色方式。下面的实例代码仅是对上例图形进行着色:1 #include <vtkAutoInit.h> 2 VTK_MODULE_INIT(vtkRenderingOpenGL); 3 4 #include <vtkSmartPointer.h> 5 #include <vtkPoints.h> 6 #include <vtkPolygon.h> 7 #include <vtkTriangle.h> 8 #include <vtkCellArray.h> 9 #include <vtkPolyData.h> 10 #include <vtkUnsignedCharArray.h> //Attribution 11 #include <vtkPointData.h> 12 #include <vtkCellData.h> 13 /// 14 #include <vtkPolyDataMapper.h> 15 #include <vtkActor.h> 16 #include <vtkRenderer.h> 17 #include <vtkRenderWindow.h> 18 #include <vtkRenderWindowInteractor.h> 19 20 int main() 21 { 22 //几何结构数据:点集 23 vtkSmartPointer<vtkPoints> pts = 24 vtkSmartPointer<vtkPoints>::New(); 25 pts->InsertNextPoint(0.0, 0.0, 0.0); 26 pts->InsertNextPoint(1.0, 0.0, 0.0); 27 pts->InsertNextPoint(1.0, 1.0, 0.0); 28 pts->InsertNextPoint(0.0, 1.0, 0.0); 29 pts->InsertNextPoint(2.0, 0.0, 0.0); 30 //拓扑结构数据:正四边形 31 vtkSmartPointer<vtkPolygon> polygon = 32 vtkSmartPointer<vtkPolygon>::New(); 33 polygon->GetPointIds()->SetNumberOfIds(4); 34 polygon->GetPointIds()->SetId(0, 0); 35 polygon->GetPointIds()->SetId(1, 1); 36 polygon->GetPointIds()->SetId(2, 2); 37 polygon->GetPointIds()->SetId(3, 3); 38 //拓扑结构数据:三角形 39 vtkSmartPointer<vtkTriangle> triangle = 40 vtkSmartPointer<vtkTriangle>::New(); 41 triangle->GetPointIds()->SetId(0, 1); 42 triangle->GetPointIds()->SetId(1, 2); 43 triangle->GetPointIds()->SetId(2, 4); 44 //构成拓扑结构集合 45 vtkSmartPointer<vtkCellArray> cells = 46 vtkSmartPointer<vtkCellArray>::New(); 47 cells->InsertNextCell(polygon); 48 cells->InsertNextCell(triangle); 49 //合成几何拓扑结构用于显示 50 vtkSmartPointer<vtkPolyData> polygonPolyData = 51 vtkSmartPointer<vtkPolyData>::New(); 52 polygonPolyData->SetPoints(pts); 53 polygonPolyData->SetPolys(cells); 54 55 //添加属性结构 56 unsigned char red[3] = { 255, 0, 0 }; 57 unsigned char green[3] = { 0, 255, 0 }; 58 unsigned char blue[3] = { 0, 0, 255 }; 59 vtkSmartPointer<vtkUnsignedCharArray> ptColor = 60 vtkSmartPointer<vtkUnsignedCharArray>::New(); 61 ptColor->SetNumberOfComponents(3); 62 ptColor->InsertNextTupleValue(red); 63 ptColor->InsertNextTupleValue(green); 64 ptColor->InsertNextTupleValue(blue); 65 ptColor->InsertNextTupleValue(red); 66 ptColor->InsertNextTupleValue(green); 67 polygonPolyData->GetPointData()->SetScalars(ptColor); 68 69 vtkSmartPointer<vtkUnsignedCharArray> cellColor = 70 vtkSmartPointer<vtkUnsignedCharArray>::New(); 71 cellColor->SetNumberOfComponents(3); 72 cellColor->InsertNextTupleValue(blue); 73 cellColor->InsertNextTupleValue(red); 74 polygonPolyData->GetCellData()->SetScalars(cellColor); 75 76 vtkSmartPointer<vtkPolyDataMapper> mapper = 77 vtkSmartPointer<vtkPolyDataMapper>::New(); 78 mapper->SetInputData(polygonPolyData); 79 80 vtkSmartPointer<vtkActor> actor = 81 vtkSmartPointer<vtkActor>::New(); 82 actor->SetMapper(mapper); 83 84 vtkSmartPointer<vtkRenderer> render = 85 vtkSmartPointer<vtkRenderer>::New(); 86 render->AddActor(actor); 87 render->SetBackground(0.0, 0.0, 0.0); 88 89 vtkSmartPointer<vtkRenderWindow> rw = 90 vtkSmartPointer<vtkRenderWindow>::New(); 91 rw->AddRenderer(render); 92 rw->SetSize(320, 240); 93 rw->SetWindowName("Creating PolyData Structure"); 94 95 vtkSmartPointer<vtkRenderWindowInteractor> rwi = 96 vtkSmartPointer<vtkRenderWindowInteractor>::New(); 97 rwi->SetRenderWindow(rw); 98 rwi->Render(); 99 rwi->Start(); 100 101 return 0; 102 }
该示例代码继承了上一节中的vtkPolyData数据。定义了两个vtkUnsignedCharArray对象ptColor和cellColor,分别为点和单元设置颜色数据。vtkUnsignedCharArray对象实际上为一个Unsigned char类型的数组。SetNumberOfComponents()函数指定了该数组中每个元组的大小。由于每个颜色是由RGB三个颜色分量组成。因此设置元组大小为3。InsertNextTupleValue()函数可以顺序插入元组数据。由于要为点集设置颜色,因此颜色数目要与点数保持一致。对于单元的颜色数据设置同样需要注意,有多少个单元,就要设置多少个颜色。设置点的颜色时,需要通过GetPointData()函数获取vtkPointData类型的点数据指针,然后通过SetScalar()函数设置颜色数据。显示结果如下所示,从图中可以看出,在进行颜色渲染时,使用的是点的颜色,而不是单元的颜色!根据之前的内容,我们知道:点数据和单元的属性数据是分别存储在VTKPointData和VTKCellData中的。在这里,我们还是要着重区分一下标量属性数据和向量属性数据的区别:向量数据具有方向和模;而标量数据不具有。如果一个数据(可以是单组分、也可以是多组分)经过一个几何变换后保持不变,那么该数据是一个标量,例如我们说的颜色属性。然而,对于法向量,该数据同样有三个组分,却是一个矢量属性,因为法向量经过几何变换后(如图像旋转)会发生改变。因此不能简单滴通过组分的个数来区分标量数据或者矢量数据。在对属性数据进行赋值时,也要分清标量数据还是矢量数据,不能将两者混淆,例如将颜色数据设置为矢量数据,那么在对图像数据进行几何变换后,颜色数据会发生改变。
2. 单元的标量属性和矢量属性数据设置
默认情况下,vtkPolyDataMapper会使用一个unsigned char类型的三元数组作为颜色值进行渲染。但是在很多情况下,模型颜色是通过属性数据获取的,比如根据标量数据在颜色查找表中获取相应的颜色。1 #include <vtkAutoInit.h> 2 VTK_MODULE_INIT(vtkRenderingOpenGL); 3 4 #include <vtkSmartPointer.h> 5 #include <vtkPlaneSource.h> 6 #include <vtkPolyData.h> 7 #include <vtkFloatArray.h> 8 #include <vtkCellData.h> 9 #include <vtkLookupTable.h> 10 #include <vtkPolyDataMapper.h> 11 #include <vtkActor.h> 12 #include <vtkRenderer.h> 13 #include <vtkRenderWindow.h> 14 #include <vtkRenderWindowInteractor.h> 15 16 int main() 17 { 18 vtkSmartPointer<vtkPlaneSource> gridSource = 19 vtkSmartPointer<vtkPlaneSource>::New(); 20 gridSource->SetXResolution(3); 21 gridSource->SetYResolution(3); 22 gridSource->Update(); 23 24 vtkSmartPointer<vtkPolyData> grid = gridSource->GetOutput(); 25 //标量属性 26 vtkSmartPointer<vtkFloatArray> cellScalars = 27 vtkSmartPointer<vtkFloatArray>::New(); 28 //矢量属性 29 vtkSmartPointer<vtkFloatArray> cellVector = 30 vtkSmartPointer<vtkFloatArray>::New(); 31 cellVector->SetNumberOfComponents(3); 32 //设置属性 33 for (int i = 0; i < 9; i++) 34 { 35 cellScalars->InsertNextValue(i + 1); //九个索引 36 cellVector->InsertNextTuple3(0.0, 0.0, 1.0); 37 } 38 39 grid->GetCellData()->SetScalars(cellScalars); 40 grid->GetCellData()->SetVectors(cellVector); 41 42 vtkSmartPointer<vtkLookupTable> lut = 43 vtkSmartPointer<vtkLookupTable>::New(); 44 lut->SetNumberOfTableValues(10); 45 lut->Build(); 46 lut->SetTableValue(0, 0, 0, 0, 1); 47 lut->SetTableValue(1, 0.8900, 0.8100, 0.3400, 1); 48 lut->SetTableValue(2, 1.0000, 0.3882, 0.2784, 1); 49 lut->SetTableValue(3, 0.9608, 0.8706, 0.7020, 1); 50 lut->SetTableValue(4, 0.9020, 0.9020, 0.9804, 1); 51 lut->SetTableValue(5, 1.0000, 0.4900, 0.2500, 1); 52 lut->SetTableValue(6, 0.5300, 0.1500, 0.3400, 1); 53 lut->SetTableValue(7, 0.9804, 0.5020, 0.4471, 1); 54 lut->SetTableValue(8, 0.7400, 0.9900, 0.7900, 1); 55 lut->SetTableValue(9, 0.2000, 0.6300, 0.7900, 1); 56 // 57 vtkSmartPointer<vtkPolyDataMapper> mapper = 58 vtkSmartPointer<vtkPolyDataMapper>::New(); 59 mapper->SetInputData(grid); 60 mapper->SetScalarRange(0, 9); 61 mapper->SetLookupTable(lut); 62 // 63 vtkSmartPointer<vtkActor> actor = 64 vtkSmartPointer<vtkActor>::New(); 65 actor->SetMapper(mapper); 66 67 vtkSmartPointer<vtkRenderer> render = 68 vtkSmartPointer<vtkRenderer>::New(); 69 render->AddActor(actor); 70 render->SetBackground(0.0, 0.0, 0.0); 71 72 vtkSmartPointer<vtkRenderWindow> rw = 73 vtkSmartPointer<vtkRenderWindow>::New(); 74 rw->AddRenderer(render); 75 rw->SetSize(320, 320); 76 rw->SetWindowName("Setting Attribution of Vectors and Scalars"); 77 78 vtkSmartPointer<vtkRenderWindowInteractor> rwi = 79 vtkSmartPointer<vtkRenderWindowInteractor>::New(); 80 rwi->SetRenderWindow(rw); 81 rwi->Start(); 82 83 return 0; 84 }
这个实例主要演示了怎样添加单元的标量属性数据和向量属性数据。VTKPlaneSource定义了一个3*3的网格数据。cellScalars定义了vtkFloatArray类型的标量属性数据,默认情况下其元组大小为1,因此不需要显示指定其大小;cellVectors则定义了vtkFloatArray类型的矢量属性数组,通过SetNumberOfComponents()指定向量维数为3,并通过InsertNextTuple3()可以方便地插入向量数据。定义完毕后,vtkPolyData的单元数据(VTKCellData)通过调用函数SetScalars()和SetVectors()设置相应的属性数据和标量数据。为了进一步演示利用标量数据进行颜色映射,定义了一个具有10个颜色的颜色映射表。利用vtkPolyDataMapper类的SetLookupTable()函数设置定义的颜色映射表。另外,需要注意的是,SetScalarRange()指定了颜色映射范围的最小值和最大值,当标量值大于最大值时,按定义最大值获取颜色;当小鱼最小值时,按照指定的最小值获取颜色。这样做的一个好处是,可以在一个小的标量范围内显示更多的颜色!!!该例的输出结果为: