Использовать текстуру, визуализированную вне экрана Qt3D, в OpenGL

Цель

Я хотел бы реализовать настоящий виджет для Qt3D, поскольку QWidget::createWindowContainer просто не подходит для меня.


описание проблемы

Мой первый подход, позволяющий создать новый подкласс класса QWidget и QSurface, не увенчался успехом, поскольку код Qt3D либо ожидает QWindow, либо QOffscreenSurface в несколько places, и я не хочу перекомпилировать всю базу Qt3D.

Моя вторая идея заключалась в том, чтобы визуализировать содержимое Qt3D на внеэкранной поверхности, а затем нарисовать текстуру на четырехугольнике в QOpenGLWidget. Когда я использую узел QRenderCapture framegraph для сохранения изображения, отрендеренного в закадровую текстуру, а затем загружаю изображение в QOpenGLTexture и рисую его с помощью функции QOpenGLWidget paintGL, я вижу визуализированное изображение - т. Е. рендеринг в Qt3D, а также в виджете OpenGL работает правильно. Это просто очень медленно по сравнению с рендерингом содержимого напрямую из Qt3D.

Теперь, когда я использую GLuint, возвращаемый QTexutre2D для привязки текстуры во время рендеринга QOpenGLWidget, все остается черным.

Конечно, это имело бы смысл, если бы контексты QOpenGLWidget и QT3D были полностью разделены. Но, получив AbstractRenderer из _ 16_ Мне удалось получить контекст, который Qt3D использует. В моем main.cpp я поставил

QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);

Контекст QOpenGLWidget и Qt3D оба ссылаются на один и тот же общий контекст - я проверил это, распечатав оба с использованием qDebug, это один и тот же объект.

Разве это не должно позволить мне использовать текстуру из Qt3D?

Или какие-либо другие предложения о том, как реализовать такой виджет? Я просто подумал, что это самый простой способ.


Детали реализации / Что я пробовал до сих пор

Вот как выглядит функция paintGL в моем QOpenGLWidget:

glClearColor(1.0, 1.0, 1.0, 1.0);
glDisable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

d->m_shaderProgram->bind();
{
    QMatrix4x4 m;
    m.ortho(0, 1, 1, 0, 1.0f, 3.0f);
    m.translate(0.0f, 0.0f, -2.0f);

    QOpenGLVertexArrayObject::Binder vaoBinder(&d->m_vao);

    d->m_shaderProgram->setUniformValue("matrix", m);
    glBindTexture(GL_TEXTURE_2D, d->m_colorTexture->handle().toUInt());
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
d->m_shaderProgram->release();

m_colorTexture - это QTexture2D, который прикреплен к _26 _ / _ 27_, в который Qt3D визуализирует сцену за кадром.

У меня есть QFrameAction для запуска обновлений розыгрыша на QOpenGLWidget:

connect(d->m_frameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qt3DWidget::paintGL);

Я убедился, что это действительно вызывает функцию paintGL. Итак, каждый раз, когда я рисую QOpenGLWidget, рамка должна быть готова и присутствовать в текстуре.

Я также попытался заменить m_colorTexture на QSharedGLTexture. Затем я создал эту текстуру в контексте QOpenGLWidget, как это

m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
m_texture->setFormat(QOpenGLTexture::RGBA8_UNorm);
// w and h are width and height of the widget
m_texture->setSize(w, h);
// m_colorTexture is the QSharedGLTexture
m_colorTexture->setTextureId(m_texture->textureId());

В resizeEvent функции QOpenGLWdiget я установил соответствующий размер для этой текстуры, а также для всех закадровых ресурсов Qt3D. Это тоже показывает просто черный экран. Размещение qDebug() << glGetError(); непосредственно после привязки текстуры просто показывает 0 каждый раз, поэтому я предполагаю, что ошибок нет.


Код можно найти здесь, в моем проекте GitHub.


person Florian Blume    schedule 01.09.2020    source источник
comment
Я разработал довольно большое приложение QML / Qt3D с этим: Может ли это сработать для вас? `#include ‹QGuiApplication› #include ‹QQmlApplicationEngine› int main (int argc, char * argv []) {Приложение QApplication (argc, argv); QQmlApplicationEngine engine; engine.load (QUrl (QStringLiteral (qrc: /res/main.qml))); вернуть app.exec (); } import QtQuick 2.8 import QtQuick.Controls 2.3 import QtQuick.Scene3D 2.0 ApplicationWindow {Scene3D {}} `   -  person Alexander V    schedule 03.09.2020
comment
Спасибо за ваш комментарий. Вы правы, на самом деле я уже думал о переходе на QML, но остальная часть моего приложения (то, что Я на самом деле разрабатываю) не в QML, поэтому мне пришлось бы много адаптироваться - и я подумал, что реализация виджета Qt3D не займет так много времени. Кроме того, мне нужно, чтобы трехмерный виджет можно было перемещать с помощью мыши (аналогично перетаскиванию квадрата в MS Paint, надеюсь, вы понимаете, что я имею в виду).   -  person Florian Blume    schedule 03.09.2020
comment
Приложение гибридного режима кажется возможным. Однако большая часть моего кода написана на C ++. Но часть QML многое упрощает.   -  person Alexander V    schedule 03.09.2020
comment
Как насчет doc.qt.io/archives/qt-5.5/qt3d- qwindow.html Однако предоставлено не так много информации.   -  person Alexander V    schedule 03.09.2020
comment
Дело в том, что окно реализует QSurface (потому что QWindow это делает, а Qt3DWindow является подклассом). Теперь я подумал о создании подклассов QWidget и QSurface и передаче поверхности QRenderSurfaceSelector Qt3D, но я действительно не знаю, как это реализовать (например, функция, которая возвращает QPlatformSurface *), а Qt3D внутренне ожидает QWindow или QOffscreenSurface, т. Е. Я бы приходится адаптировать много кода Qt3D, что делает невозможным.   -  person Florian Blume    schedule 04.09.2020
comment
Прямо сейчас я нахожусь в точке, где я вручную запрашиваю QAspectEngine для обработки кадров и напрямую использую тот же контекст в QOpenGLWidget и Qt3d, который по крайней мере один раз рисует чистый цвет Qt3D, хотя впоследствии он больше не обновляется. Я до сих пор не понимаю, почему я не могу использовать текстуру из Qt3D с общим контекстом ...   -  person Florian Blume    schedule 04.09.2020


Ответы (1)


Обновление (10 мая 2021 г., поскольку я снова наткнулся на свой ответ):

Моя реализация Qt3DWidget работает точно так же, как и в моем вопросе здесь, проблема заключалась в том, что мне пришлось позвонить update() когда вместо paintGL было запущено действие кадра (да, глупый я, я действительно знаю это).


Хотя я не нашел точного решения своего вопроса, я опубликую здесь ответ, поскольку мне удалось создать виджет Qt3D.

Код можно найти здесь. Это не самое чистое решение, потому что я думаю, что можно каким-то образом использовать общую текстуру. Вместо этого теперь я устанавливаю контекст QOpenGLWidget на Qt3D, для которого я должен использовать частные классы Qt3D. Это означает, что Qt3D рисует непосредственно в буфер кадра, связанный с виджетом OpenGL. К сожалению, теперь виджет должен быть драйвером рендеринга и вручную обновлять QAspectEngine, вызывая processFrame. В идеале я хотел бы оставить все циклы обработки на Qt3D, но, по крайней мере, теперь виджет работает как есть.

Изменить:

Я нашел пример для QSharedGLTexture в ручных тестах, здесь. Он работает наоборот, то есть OpenGL отображает текстуру, а Qt3D использует ее, поэтому я предполагаю, что можно изменить направление. К сожалению, QSharedGLTexture кажется немного нестабильным, поскольку изменение размера окна OpenGL иногда приводит к сбою приложения. Вот почему я пока буду придерживаться своего решения. Но если у кого-то есть новости по этому поводу, не стесняйтесь публиковать ответ!

person Florian Blume    schedule 04.09.2020