2014-07-02 44 views
7

Występuje pewien problem podczas próby uruchomienia wielu okien renderowania w aplikacji Python VTK, którą piszę. Aplikacja jest próbą renderowania modelu 3D w dwóch osobnych widokach dla aplikacji stereo (tj. Lewy render i prawy render), ale mam problem z aktualizowaniem kamer każdego okna jednocześnie. Obecnie mam dwa prawie identyczne skonfigurowane potoki, z których każdy ma własny vtkCamera, vtkRenderWindow, vtkRenderer i vtkRenderWindowInteractor, z tą różnicą, że prawa kamera jest przesunięta w kierunku 30 jednostek wzdłuż osi X.Pozycja aktualizacji VTK wielu okien renderowania

Każdy z interwałów okna renderowania jest aktualizowany za pomocą metody vtkRenderWindowInteractor.AddObserver(), która wywołuje prostą funkcję resetowania kamer do ich oryginalnych pozycji i orientacji. Największym problemem jest to, że zdaje się, że dzieje się to tylko w jednym oknie na raz, a konkretnie w tym samym oknie. To tak, jakby zegar interaktora po prostu się wyłączył, gdy interaktor straci ostrość. Ponadto, gdy przytrzymam mysz (i tym samym przesuniemy kamerę), renderowany obraz zaczyna "dryfować", resetując się do coraz mniejszej poprawnej pozycji, mimo że wczytałem współrzędne do tej funkcji.

Oczywiście jestem bardzo nowy w VTK, a wiele z tego, co się dzieje, jest dość mylące, ponieważ wiele z nich jest ukrytych w zapleczu, więc byłoby wspaniale uzyskać pomoc w tej sprawie. Mój kod znajduje się poniżej. Dzięki chłopaki!

from vtk import* 
from parse import * 
import os 
import time, signal, threading 

def ParseSIG(signum, stack): 
     print signum 
     return 

class vtkGyroCallback(): 
     def __init__(self): 
       pass 
     def execute(self, obj, event): 
       #Modified segment to accept input for leftCam position 
       gyro = (raw_input()) 
       xyz = parse("{} {} {}", gyro) 
       #This still prints every 100ms, but camera doesn't update! 
       print xyz 

       #These arguments are updated and the call is made. 
       self.leftCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2])) 
       self.leftCam.SetFocalPoint(0,0,0) 
       self.leftCam.SetViewUp(0,1,0) 
       self.leftCam.OrthogonalizeViewUp() 

       self.rightCam.SetPosition(10, 40, 100) 
       self.rightCam.SetFocalPoint(0,0,0) 
       self.rightCam.SetViewUp(0,1,0) 
       self.rightCam.OrthogonalizeViewUp() 

       #Just a guess 
       obj.Update() 
       return 

def main(): 

     # create two cameras 
     cameraR = vtkCamera() 
     cameraR.SetPosition(0,0,200) 
     cameraR.SetFocalPoint(0,0,0) 

     cameraL = vtkCamera() 
     cameraL.SetPosition(40,0,200) 
     cameraL.SetFocalPoint(0,0,0) 



     # create a rendering window and renderer 
     renR = vtkRenderer() 
     renR.SetActiveCamera(cameraR) 

     renL = vtkRenderer() 
     renL.SetActiveCamera(cameraL) 

     # create source 
     reader = vtkPolyDataReader() 
     path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk" 
     reader.SetFileName(path) 
     reader.Update() 

     # create render window 

     renWinR = vtkRenderWindow() 
     renWinR.AddRenderer(renR) 
     renWinR.SetWindowName("Right") 

     renWinL = vtkRenderWindow() 
     renWinL.AddRenderer(renL) 
     renWinL.SetWindowName("Left") 

     # create a render window interactor 
     irenR = vtkRenderWindowInteractor() 
     irenR.SetRenderWindow(renWinR) 

     irenL = vtkRenderWindowInteractor() 
     irenL.SetRenderWindow(renWinL) 

     # mapper 
     mapper = vtkPolyDataMapper() 
     mapper.SetInput(reader.GetOutput()) 

     # actor 
     actor = vtkActor() 
     actor.SetMapper(mapper) 

     # assign actor to the renderer 
     renR.AddActor(actor) 
     renL.AddActor(actor) 

     # enable user interface interactor 
     renWinR.Render() 
     renWinL.Render() 
     irenR.Initialize() 
     irenL.Initialize() 

     #Create callback object for camera manipulation 
     cb = vtkGyroCallback() 
     cb.rightCam = cameraR 
     cb.leftCam = cameraL 
     renWinR.AddObserver('InteractionEvent', cb.execute) 
     renWinL.AddObserver('InteractionEvent', cb.execute) 
     irenR.AddObserver('TimerEvent', cb.execute) 
     irenL.AddObserver('TimerEvent', cb.execute) 
     timerIDR = irenR.CreateRepeatingTimer(100) 
     timerIDL = irenL.CreateRepeatingTimer(100) 

     irenR.Start() 
     irenL.Start() 

if __name__ == '__main__': 
    main() 

EDIT:

Po dalsze przeglądanie Wydaje się, że TimerEvents nie strzelają więcej niż raz z rzędu po MouseClickEvent i nie mam pojęcia dlaczego.

EDYCJA 2: Zarysuj to, są one absolutnie wyzwalane, jak na niektórych wynikach testu osadzonych w kodzie. Zmodyfikowałem kod, aby zaakceptować dane wejściowe użytkownika dla wywołania self.leftCam.SetPosition() w metodzie vtkGyroCallback.execute() (W ten sposób zastąpienie zakodowanych na stałe "10, 40, 100" parametrów trzema zmiennymi wejściowymi), a następnie wyprowadziłem wyjście skryptu, który po prostu wyświetlał trzy przypadkowe wartości w moim głównym program. To, co osiągnęło, to miało okno renderowania, które stale zmieniało położenie. Zamiast tego nic się nie dzieje, dopóki nie kliknę ekranu, w którym to momencie zaczyna się oczekiwana funkcjonalność. Przez cały czas zdarzenia timera są nadal wyzwalane, a wejścia są nadal akceptowane, ale kamery odmawiają aktualizacji, dopóki zdarzenie myszy nie pojawi się w zasięgu ich okna. O co chodzi?

EDYCJA 3: Wykopałem trochę więcej i odkryłem, że w metodzie vtkObject::InvokeEvent(), która jest wywoływana w każdym zdarzeniu interakcji, istnieje pętla ostrości, która przesłania wszystkie obserwatory, które nie dotyczą obiektu, na którym jest fokus. Zamierzam zbadać, czy istnieje sposób na usunięcie fokusa, aby zamiast tego obejść tę pętlę ostrości i przejść do nieokreślonej pętli, która obsługuje nieostre obiekty.

+0

Wszystko stałe! Jeśli chcesz otrzymać nagrodę, powiedz mi, jak sprawić, by działała płynnie (Framerate aktualnie znajduje się na poziomie 2-3 FPS z rozwiązaniem pokazanym w moim komentarzu poniżej): D – SwarthyMantooth

+0

Złom to również rozwiązało ten problem! Powinienem zapłacić za te rzeczy;) Moja metoda używania dwóch oddzielnych 'vtkRenderWindow's była powolna i nieporęczna. Przestawiłem się na używanie dwóch 'vtkRenderer'ów wewnątrz pojedynczego' vtkRenderWindow', a framerate do 60FPS. Dodatkowo, Oculus Rift nie wymaga wielu okien, ponieważ każdy z nich jest technicznie częścią tego samego monitora, więc cała aplikacja może znajdować się w jednym oknie. – SwarthyMantooth

Odpowiedz

5

Rozwiązanie było zaskakująco proste, ale dzięki brakowi wysokiej jakości dokumentacji dostarczonej przez VTK zostałem zmuszony do przeszukania źródła, aby go znaleźć. W rzeczywistości wszystko, co musisz zrobić, to pseudo-wątek Render() wywołania z każdego z interaktorów za pośrednictwem dowolnej metody wywołania zwrotnego, której używasz do obsługi swoich TimerEvent s. Zrobiłem to za pomocą właściwości ID dodanych do każdego interaktora (widoczne w kodzie podanym poniżej). Widać, że za każdym razem, gdy TimerEvent jest uruchamiany z wewnętrznego zegara interaktora (irenR obsługuje prawe oko), wywoływana jest funkcja irenL 'i na odwrót.

Aby rozwiązać ten problem, po raz pierwszy zdałem sobie sprawę, że standardowe funkcje interakcyjne (zdarzenia myszy i tym podobne) działają normalnie. Więc wykopałem źródła w vtkRenderWindowInteractor.cxx i zdali sobie sprawę, że metody te zostały wyodrębnione do poszczególnych implementacji vtkInteractorStyle. Po zrootowaniu w źródle vtkInteractorStyleTrackball.cxx odkryłem, że faktycznie istnieje funkcja Render() w klasie vtkRenderWindowInteractor. Domyśl! Dokumentacja na pewno o tym nie wspominała!

Niestety, dwa renderowania naraz są bardzo powolne. Jeśli wykonam tę metodę za pomocą tylko jednego okna (W którym momencie staje się niepotrzebne), działa to cudownie. Framerate czołgi z drugim oknem. No cóż, co możesz zrobić?

Oto mój kod skorygowane (wreszcie mogę rozpocząć pracę na co ja powinien się rozwijać):

from vtk import* 
from parse import * 
import os 
import time, signal, threading 

def ParseSIG(signum, stack): 
     print signum 
     return 

class vtkGyroCallback(): 
     def __init__(self): 
       pass 
     def execute(self, obj, event): 
       #Modified segment to accept input for leftCam position 
       gyro = (raw_input()) 
       xyz = parse("{} {} {}", gyro) 
       #print xyz 



       # "Thread" the renders. Left is called on a right TimerEvent and right is called on a left TimerEvent. 
       if obj.ID == 1 and event == 'TimerEvent': 
         self.leftCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2])) 
         self.irenL.Render() 
         #print "Left" 
       elif obj.ID == 2 and event == 'TimerEvent': 
         self.rightCam.SetPosition(float(xyz[0]), float(xyz[1]), float(xyz[2])) 
         self.irenR.Render() 
         #print "Right" 
       return 

def main(): 

     # create two cameras 
     cameraR = vtkCamera() 
     cameraR.SetPosition(0,0,200) 
     cameraR.SetFocalPoint(0,0,0) 

     cameraL = vtkCamera() 
     cameraL.SetPosition(40,0,200) 
     cameraL.SetFocalPoint(0,0,0) 



     # create a rendering window and renderer 
     renR = vtkRenderer() 
     renR.SetActiveCamera(cameraR) 

     renL = vtkRenderer() 
     renL.SetActiveCamera(cameraL) 

     # create source 
     reader = vtkPolyDataReader() 
     path = "/home/compilezone/Documents/3DSlicer/SlicerScenes/LegoModel-6_25/Model_5_blood.vtk" 
     reader.SetFileName(path) 
     reader.Update() 

     # create render window 

     renWinR = vtkRenderWindow() 
     renWinR.AddRenderer(renR) 
     renWinR.SetWindowName("Right") 

     renWinL = vtkRenderWindow() 
     renWinL.AddRenderer(renL) 
     renWinL.SetWindowName("Left") 

     # create a render window interactor 
     irenR = vtkRenderWindowInteractor() 
     irenR.SetRenderWindow(renWinR) 

     irenL = vtkRenderWindowInteractor() 
     irenL.SetRenderWindow(renWinL) 

     # mapper 
     mapper = vtkPolyDataMapper() 
     mapper.SetInput(reader.GetOutput()) 

     # actor 
     actor = vtkActor() 
     actor.SetMapper(mapper) 

     # assign actor to the renderer 
     renR.AddActor(actor) 
     renL.AddActor(actor) 


     # enable user interface interactor 
     renWinR.Render() 
     renWinL.Render() 
     irenR.Initialize() 
     irenL.Initialize() 

     #Create callback object for camera manipulation 
     cb = vtkGyroCallback() 
     cb.rightCam = renR.GetActiveCamera()#cameraR 
     cb.leftCam = renL.GetActiveCamera()#cameraL 
     cb.irenR = irenR 
     cb.irenL = irenL 

     irenR.ID = 1 
     irenL.ID = 2 
     irenR.AddObserver('TimerEvent', cb.execute) 
     irenL.AddObserver('TimerEvent', cb.execute) 
     timerIDR = irenR.CreateRepeatingTimer(100) 
     timerIDL = irenL.CreateRepeatingTimer(100) 

     irenL.Start() 
     irenR.Start() 

if __name__ == '__main__': 

     main() 
+0

Zabawny fakt ponad dwa lata później: Możesz użyć tylko jednego okna z wieloma renderers wewnątrz za pomocą funkcji setViewport. Nie mam na sobie kodu (Prawdopodobnie nadal jest na pewnym szkolnym dysku twardym 700 mil od hotelu), ale po wykonaniu tej czynności liczba klatek na sekundę nie jest problemem. Sprawdź [oficjalny przykład] (http://www.vtk.org/Wiki/VTK/Examples/Cxx/Visualization/MultipleViewports) od facetów VTK, aby lepiej zrozumieć, co mam na myśli. – SwarthyMantooth