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等交互器樣式使用方法一致。