1.拾取
选择拾取是人机交互过程的一个重要功能。
一个最经典的例子就是,在玩3D游戏时,场景中可能会存在多个角色,有时需要用鼠标来选择所要控制的角色,这就要用到拾取功能。
另外,在某些三维图形的编辑软件中,经常需要编辑其中的一个点、一个面片或者一个局部区域,这也需要通过拾取功能来完成。
VTK中定义了多个拾取功能的类,具体的继承关系如下:
VTK中所有的拾取类都继承自vtkAbstractPicker类,在这些类的基础之上可以实现非常复杂的功能。
2.点拾取
从上图中能够知晓,完成点拾取功能的类是vtkPointPicker。
vtk中的消息是通过vtkRenderWindowInteractor类处理的,在类vtkRenderWindowInteractor中,定义如下函数:
virtual void SetPicker(vtkAbstractPicker* );
该函数用来设置具体的VTKAbstractPicker对象,并执行相应的拾取操作。因此对于点拾取,实际就是设置VTKPointPicker的过程。
之前,曾经细致的研究过,vtkRenderWindowInteractor内部定义了一个vtkInteractorStyle对象。vtkInteractorStyle类是一个虚基类,其子类定义了多种鼠标和键盘消息的处理方法,在实现拾取操作是,需要定制相应的鼠标消息处理函数。比如拾取某个点时,应该响应鼠标的左键按下消息,并在响应该消息的函数中根据鼠标的当前窗口坐标来完成拾取操作。
点拾取的示例代码如下:
#include <vtkAutoInit.h> VTK_MODULE_INIT(vtkRenderingOpenGL) VTK_MODULE_INIT(vtkRenderingFreeType) VTK_MODULE_INIT(vtkInteractionStyle) #include <vtkSmartPointer.h> #include <vtkSphereSource.h> #include <vtkPolyDataMapper.h> #include <vtkActor.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkPointPicker.h> //this->Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer() #include <vtkRendererCollection.h> #include <vtkInteractorStyleTrackballCamera.h> #include <vtkObjectFactory.h> //vtkStandardNewMacro(); #include <vtkProperty.h> #include <vtkAxesActor.h> #include <vtkOrientationMarkerWidget.h> /**************************************************************************************************/ class PointPickerInteractorStyle : public vtkInteractorStyleTrackballCamera { public: static PointPickerInteractorStyle* New(); vtkTypeMacro(PointPickerInteractorStyle, vtkInteractorStyleTrackballCamera); virtual void OnLeftButtonDown() { //打印鼠标左键像素位置 std::cout << "Picking pixel: " << this->Interactor->GetEventPosition()[0] << " " << this->Interactor->GetEventPosition()[1] << std::endl; //注册拾取点函数 this->Interactor->GetPicker()->Pick( this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1], 0, // always zero. this->Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer() ); //打印拾取点空间位置 double picked[3]; this->Interactor->GetPicker()->GetPickPosition(picked); std::cout << "Picked value: " << picked[0] << " " << picked[1] << " " << picked[2] << std::endl; //对拾取点进行标记 vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New(); sphereSource->Update(); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(sphereSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); actor->SetPosition(picked); actor->SetScale(0.05); actor->GetProperty()->SetColor(1.0, 0.0, 0.0); this->Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(actor); vtkInteractorStyleTrackballCamera::OnLeftButtonDown(); } }; /**************************************************************************************************/ vtkStandardNewMacro(PointPickerInteractorStyle); int main() { vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New(); sphereSource->Update(); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(sphereSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); renderer->SetBackground(1, 1, 1); vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->Render(); renderWindow->SetWindowName("PointPicker"); renderWindow->AddRenderer(renderer); vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New(); vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); renderWindowInteractor->SetPicker(pointPicker); renderWindowInteractor->SetRenderWindow(renderWindow); vtkSmartPointer<PointPickerInteractorStyle> style = vtkSmartPointer<PointPickerInteractorStyle>::New(); renderWindowInteractor->SetInteractorStyle(style); / vtkSmartPointer<vtkAxesActor> Axes = vtkSmartPointer<vtkAxesActor>::New(); vtkSmartPointer<vtkOrientationMarkerWidget> widget = vtkSmartPointer<vtkOrientationMarkerWidget>::New(); widget->SetInteractor(renderWindowInteractor); widget->SetOrientationMarker(Axes); widget->SetOutlineColor(1, 1, 1); widget->SetViewport(0, 0, 0.2, 0.2); widget->SetEnabled(1); widget->InteractiveOn(); renderWindow->Render(); renderWindowInteractor->Start(); return 0; }
实际操作细节分析:
- vtkInteractorStyleTrackballCemera派生类设计
PointPickerInteractorStyle类从vtkInteractorStyleTrackballCemera派生,并覆盖了该类OnLeftButtonDown()函数。在该函数中,调用了vtkRenderWindowInteractor的GetEventPosition()函数输出鼠标点击的屏幕坐标。
- 拾取函数Pick()设计
int Pick(double selectionX, double selectionY, double selectionZ, vtkRender* renderer);
该函数需要接受四个参数,前三个为(selectionX,selectionY,selectionZ),即鼠标的当前窗口坐标,其中selectionZ通常为零。最后一个是vtkRenderer对象。
- GetPackPosition()是指世界坐标系下拾取点的坐标
- mian()函数中设计拾取调用流程
vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New(); vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); renderWindowInteractor->SetPicker(pointPicker); renderWindowInteractor->SetRenderWindow(renderWindow); vtkSmartPointer<PointPickerInteractorStyle> style = vtkSmartPointer<PointPickerInteractorStyle>::New(); renderWindowInteractor->SetInteractorStyle(style);
实例化vtkPointPicker对象以后,调用vtkRenderWindowInteractor::SetPicker()函数将其设置到渲染窗口交互器中。PointPickerInteractorStyle类与vtkInteractorStyleImage等交互器样式使用方法一致。