VTK更新多个渲染窗口的位置

8
我在编写一个Python VTK应用程序时,尝试运行多个渲染窗口时遇到了一些问题。该应用程序旨在为立体应用程序(即左渲染和右渲染)呈现一个3D模型的两个分离视图,但我在同时更新每个窗口的相机时遇到了问题。我目前设置了两个几乎相同的管道,每个管道都有自己的vtkCamera、vtkRenderWindow、vtkRenderer和vtkRenderWindowInteractor,唯一的区别是右侧相机在X轴上位置偏移30个单位。
通过调用一个简单的函数重置相机到它们的原始位置和方向, vtkRenderWindowInteractor.AddObserver()方法更新每个渲染窗口交互器。最大的问题是这似乎只在一个窗口上发生,特别是那个时刻处于焦点的窗口。这就好像交互器的计时器在失去焦点后就关闭了一样。此外,当我按住鼠标(因此移动相机),渲染出的图像开始“漂移”,尽管我已经将坐标硬编码到函数中,但其位置会越来越不正确。
显然我对VTK非常陌生,许多东西都很混乱,因为许多东西都隐藏在后台,所以获取一些关于此事的帮助将是惊人的。我的代码如下。谢谢大家!
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()

编辑:

经过进一步观察,似乎在鼠标单击事件之后,计时器事件不会连续触发多次,我不知道为什么。

编辑2: 取消上面所说的,根据我嵌入代码中的一些测试输出,它们肯定是在触发。我修改了代码,使其接受用户输入作为vtkGyroCallback.execute()方法中的self.leftCam.SetPosition()调用(因此使用三个输入变量替换了硬编码的“10, 40, 100”参数),然后将一个简单显示三个随机值的脚本的输出导入到我的主程序中。这个操作应该让渲染窗口的位置不断改变。但实际上,除非我点击屏幕,否则什么都不会发生,此时期望的功能开始出现。整个时间,计时器事件仍在触发,输入仍在被接受,但相机拒绝更新,直到窗口内发生鼠标事件。是怎么回事呢?

编辑3: 我继续研究发现,在每个交互事件中调用的vtkObject::InvokeEvent()方法中有一个焦点循环,覆盖了所有与焦点对象无关的观察者。我将调查是否有一种方法可以去除焦点,使其绕过此焦点循环并进入处理非聚焦对象的非聚焦循环。


已经修复了!如果你想要赏金,请告诉我如何使其运行流畅(当前帧率为2-3 FPS,解决方案在下面的评论中显示):D - SwarthyMantooth
算了,我也解决了那个问题!我应该给自己发工资 ;)我的使用两个独立的 vtkRenderWindow 的方法很慢且笨重。我改用在单个 vtkRenderWindow 中使用两个 vtkRenderer,帧率轻松提高到60FPS。此外,Oculus Rift不需要多个窗口,因为每个眼睛屏幕在技术上都是同一台监视器的一部分,所以您整个应用程序可以在一个窗口中运行。 - SwarthyMantooth
1个回答

9
所以解决方案非常简单,但由于VTK提供的文档质量不足,我不得不深入源代码进行查找。实际上,你只需要通过任何回调方法伪线程每个交互器的Render()调用来解决问题。我使用添加到每个交互器的ID属性来完成这一点(在下面提供的代码中可见)。可以看到,每当从irenR交互器的内部计时器(irenR处理右眼)触发TimerEvent时,就会调用irenLRender()函数,反之亦然。
为了解决这个问题,我首先意识到标准的交互器功能(鼠标事件等)正常工作。因此,我在vtkRenderWindowInteractor.cxx的源代码中进行了研究,并意识到那些方法被抽象到了各个vtkInteractorStyle实现中。在vtkInteractorStyleTrackball.cxx源代码中寻找时,我发现实际上vtkRenderWindowInteractor类中有一个Render()函数。真是想不到!文档肯定没有提到!
不幸的是,同时进行两次渲染实际上非常缓慢。如果我只使用一个窗口进行此方法(此时变得不必要),它可以很好地运行。但使用第二个窗口时帧速率会下降。哦,无可奈何!
以下是我更正后的代码(终于可以开始开发我应该开发的东西了):
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()

1
有趣的事实,两年后:您可以使用setViewport函数在一个窗口中使用多个渲染器。我手头上没有代码(可能还在700英里外的某个学校硬盘上),但是如果以这种方式完成,则帧速率不是问题。查看VTK官方示例,以更好地了解我的意思:http://www.vtk.org/Wiki/VTK/Examples/Cxx/Visualization/MultipleViewports - SwarthyMantooth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接