在 OpenSceneGraph 场景中显示 Qt Quick 内容
Displaying Qt Quick content inside OpenSceneGraph scene
我想在我的 OpenSceneGraph 场景中的虚拟屏幕上显示我的 Qt Quick 内容。
我现在使用的方法非常低效:
- 使用 FBO (FrameBufferObject) 将 Qt Quick 渲染到屏幕外表面
- 使用 QOpenGLFramebufferObject::toImage()
下载像素
- 上传像素到 OSG
所以是GPU-CPU-GPU传输。 Source code
正确的解决方案应该以某种方式利用现有的 FBO 并能够仅在 GPU 内部传输数据。
有两种选择:
- 在Qt端创建FBO,在OSG端使用其纹理
- 在 OSG 端创建 FBO 并将其提供给 Qt Quick 渲染器
Qt部分没问题。我完全迷失了 OSG。谁能给我一些指点?
终于成功了。
总体思路:
使用 FBO 将 QtQuick 渲染为纹理 - 互联网上有一些 examples 可用。
在 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 不兼容,你几乎不能 link QOpenGLFrameBufferObject
到 osg::Texture2D
。
QOpenGLFrameBufferObject 具有 QOpenGLFrameBufferObject::texture()
方法,其中 returns opengl 纹理 ID,但 osg::Texture 自行管理所有 openGL 内容。
像 osg::Texture2D(uint textureId);
这样的东西可以帮助我们,但它不存在。
我们自己做一个吧
osg::Texture
由 osg::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
我想在我的 OpenSceneGraph 场景中的虚拟屏幕上显示我的 Qt Quick 内容。
我现在使用的方法非常低效:
- 使用 FBO (FrameBufferObject) 将 Qt Quick 渲染到屏幕外表面
- 使用 QOpenGLFramebufferObject::toImage() 下载像素
- 上传像素到 OSG
所以是GPU-CPU-GPU传输。 Source code
正确的解决方案应该以某种方式利用现有的 FBO 并能够仅在 GPU 内部传输数据。
有两种选择:
- 在Qt端创建FBO,在OSG端使用其纹理
- 在 OSG 端创建 FBO 并将其提供给 Qt Quick 渲染器
Qt部分没问题。我完全迷失了 OSG。谁能给我一些指点?
终于成功了。
总体思路:
使用 FBO 将 QtQuick 渲染为纹理 - 互联网上有一些 examples 可用。
在 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 不兼容,你几乎不能 link QOpenGLFrameBufferObject
到 osg::Texture2D
。
QOpenGLFrameBufferObject 具有 QOpenGLFrameBufferObject::texture()
方法,其中 returns opengl 纹理 ID,但 osg::Texture 自行管理所有 openGL 内容。
像 osg::Texture2D(uint textureId);
这样的东西可以帮助我们,但它不存在。
我们自己做一个吧
osg::Texture
由 osg::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