在OpenSceneGraph场景中显示Qt Quick内容

问题描述

我想在我的OpenSceneGraph场景内的虚拟屏幕上显示Qt Quick内容

我现在使用的方法效率很低:

  1. 使用FBO(FrameBufferObject)将Qt Quick渲染到屏幕外
  2. 使用QOpenGLFramebufferObject :: toImage()下载像素
  3. 将像素上传到OSG

因此是GPU-cpu-GPU传输。 Source code

适当的解决方案应该以某种方式利用现有的FBO,并能够仅在GPU内部传输数据。

存在两种选择:

  1. 在Qt端创建FBO,并在OSG端使用其纹理
  2. 在OSG端创建FBO,并将其馈送到Qt Quick渲染器

Qt部分正常。我对OSG完全迷失了。谁能为我提供一些指示?

解决方法

最后做到了。

总体思路:

  1. 使用FBO将QtQuick渲染为纹理-Internet上有一些examples

  2. 在OpenSceneGraph中使用此纹理

整个过程包括几个技巧。

上下文共享

要执行某些图形操作,我们必须初始化OpenGL全局状态,也称为上下文。

创建纹理后,上下文将存储其ID。 id并不是全局唯一的,因此在另一个上下文中创建另一个纹理时,它可能会获得相同的id,但背后具有不同的资源。

如果仅将纹理的ID传递给另一个渲染器(在不同的上下文中操作),并期望它显示您的纹理,则最终将显示另一个纹理,黑屏或崩溃。

补救措施是上下文共享,这实际上意味着共享ID。

OpenSceneGraph和Qt抽象不兼容,因此您需要告诉OSG不要使用其自己的上下文抽象。通过调用setUpViewerAsEmbeddedInWindow

代码:

OsgWidget::OsgWidget(QWidget* parent,Qt::WindowFlags flags)
    : QOpenGLWidget(parent,flags),m_osgViewer(new osgViewer::Viewer)
{
    setFormat(defaultGraphicsSettings());

    // ...

    m_osgGraphicsContext = m_osgViewer->setUpViewerAsEmbeddedInWindow(x(),y(),width(),height());
}

// osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> m_osgGraphicsContext;

从现在开始,现有的QOpenGLContext实例将用作OSG渲染的OpenGL上下文。

您将需要为QtQuick渲染创建另一个上下文并将它们设置为共享:

void Widget::initializeGL()
{
    QOpenGLContext* qmlGLContext = new QOpenGLContext(this);
    // ...
    qmlGLContext->setShareContext(context());
    qmlGLContext->create();
}

请记住,一次只能有一个活动上下文。

您的方案是:

0. create osg::Texture out of QOpenGLFrameBufferObject::texture()
1. make QtQuick context active
2. render QtQuick to texture
3. make primary (OSG) context active
4. render OSG
5. goto 1

制作适当的osg :: Texture

由于OSG和Qt API不兼容,您几乎无法将QOpenGLFrameBufferObject链接到osg::Texture2D

QOpenGLFrameBufferObject具有QOpenGLFrameBufferObject::texture()方法,该方法返回opengl纹理ID,但是osg :: Texture自行管理所有openGL内容。

osg::Texture2D(uint textureId);这样的东西可以帮助我们,但根本不存在。

我们自己做一个。

osg::Textureosg::TextureObject作为后盾,后者存储OpenGL纹理ID和其他一些数据。如果我们使用给定的纹理ID构造osg::TextureObject并将其传递给osg::Texture,后者将使用它作为自己的纹理。

代码:

void Widget::createOsgTextureFromId(osg::Texture2D* texture,int textureId)
{
    osg::Texture::TextureObject* textureObject = new osg::Texture::TextureObject(texture,textureId,GL_TEXTURE_2D);
    textureObject->setAllocated();

    osg::State* state = m_osgGraphicsContext->getState();
    texture->setTextureObject(state->getContextID(),textureObject);
}

完成演示项目here