2015-06-05 1 views
1

Мое мобильное приложение использует OpenGL под QML парадигмой.Как установить положение QQuickItem в точном кадре из C++?

Ориентация камеры моей сцены GL синхронизируется с датчиком движения (магнитометром и акселерометром) мобильного устройства. Я также показываю 2D-метку поверх этой трехмерной сцены, положение которой на экране должно соответствовать заданной трехмерной позиции в моей сцене GL. Эти метки являются регулярными элементами QML, которые я контролирую со стороны C++ моего приложения.

Для этого я делаю прямое подключение к сигналу QQuickWindow::beforeSynchronising(), где ориентация камеры синхронизируется с датчиком движения. Затем я проецирую трехмерную точку на экран, чтобы найти положение метки и, наконец, позвонить в QQuickItem::setPosition().

Сцена GL отображается как обычно, используя прямое соединение с сигналом QQuickWindow::beforeRendering().

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 сцены, и я думаю, что я понимаю, почему: Вызов QQuickItem::setPosition() из QSGRendering нити, по-видимому слишком поздно для QSGNode, чтобы получить обновление для этого кадра. Обновление запланировано для этого элемента, но оно не вступает в силу до следующего кадра. Следовательно, 1 кадровая задержка.

Вы можете подумать, что это не так уж плохо, однако это создает странный эффект и не помогает прочесть то, что на этикетке, потому что глаза также следуют за фоном (помните, что камера синхронизирована с датчиком движения устройства).

Я пробовал использовать сигнал QQuickWindow::afterAnimating(), но задержка еще больше. Это нормально, поскольку этот сигнал испускается из потока графического интерфейса пользователя, и, таким образом, мы еще меньше контролируем, в каком кадре будет синхронизироваться QSGRenderThread.

Итак, мой вопрос: как я могу достичь позиционирования элемента QML из C++ в точном фрейме? Возможно ли это с помощью QML? Было бы лучше, если бы мои ярлыки были чистыми C++ QQuickItems, и если вместо вызова setPosition() я использовал свою собственную функцию, которая гарантирует, что QSGNode получит обновление?

ответ

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 
} 
Смежные вопросы