如何在C ++的精确帧中设置QQuickItem位置?

时间:2015-06-05 21:53:10

标签: c++ qt opengl qml core-motion

我的移动应用程序使用OpenGL范例下的QML

我的GL场景的相机方向与移动设备的运动传感器(磁力计和加速度计)同步。我还在该3D场景的顶部显示2D标签,其在屏幕上的位置需要对应于我的GL场景中的给定3D位置。这些标签是我从我的应用程序的C ++端控制的常规QML项目。

为此,我直接连接到QQuickWindow::beforeSynchronising()信号,其中摄像机方向与运动传感器同步。然后我在屏幕上投射3D点以找到标签的位置,最后拨打QQuickItem::setPosition()

通过直接连接到QQuickWindow::beforeRendering()信号,GL场景照常渲染。

MyAppController::MyAppController() : QObject() {
    connect(window, SIGNAL(beforeSynchronising()), this, SLOT(sync()), Qt::DirectConnection);
    connect(window, SIGNAL(beforeRendering()), this, SLOT(render()), Qt::DirectConnection);

    m_camera = ...; // create camera
    m_scene = ...;  // create GL scene
    m_quickItem = ...; // load label QML item

    // start updating the window at a constant frame rate
    QTimer* timer = new QTimer(this);
    connect (timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(1000.0 / 60.0);   // 60 FPS
}

MyAppController::sync() {
    m_camera->syncWithMotionSensor();

    // use camera matrix to calculate the projection of a 3D point on the screen
    QPointF pos = ...;  
    m_quickItem->setPosition(pos);  // set the label position
}

MyAppController::render() {
    m_scene->draw(); // OpenGL code
}

一切都很好,除了我的标签似乎比我的GL场景晚了一帧,我想我理解为什么:从QSGRendering线程调用QQuickItem::setPosition()对于{{1}来说显然为时已晚获取该帧的更新。计划对该项目进行更新,但直到下一帧才生效。因此1帧延迟。

你可能认为它并没有那么糟糕,然而,它确实产生了一种奇怪的效果,并且无助于阅读标签上的内容,因为眼睛也跟着背景(记住相机)与设备运动传感器同步。

我尝试使用QSGNode信号,但延迟更大。这是正常的,因为此信号是从GUI线程发出的,因此我们对QSGRenderThread将同步到哪个帧的控制更少。

所以我的问题是,如何在精确的框架中实现C ++中QML项目的定位?这甚至可以使用QML吗?如果我的标签是纯C ++ QQuickItems并且如果不是调用setPosition()而是使用我自己的函数来确保QSGNode将接收更新,那会更好吗?

1 个答案:

答案 0 :(得分:1)

所以答案是在 呈现GL场景后简单地同步相机和标签的位置 。这样我们就可以确定在下一帧将考虑标签的位置。 最终解决方案如下所示:

MyAppController::sync() {
    m_scene->prepare();  // uses the camera matrix from previous frame    
    m_camera->syncWithMotionSensor();  // update camera matrix
    // use camera matrix to calculate the projection of a 3D point on the screen
    QPointF pos = ...;  
    m_quickItem->setPosition(pos);  // set the label position, will be updated on the next frame
}

MyAppController::render() {
    m_scene->draw(); // execute OpenGL code
}