I'm using QML, and wanted to run custom OpenGL code. I created a custom Widget in C++ (extending QQuickItem) and overrided the paint function().
When I run my application, the console prints
QSGContext::initialize: depth buffer support missing, expect rendering errors
QSGContext::initialize: stencil buffer support missing, expect rendering errors
And just as it predicted, I do get rendering errors. I'll use a spider model as an example. Here's what it should like
And here's what I'm getting
I also don't know exactly how to describe it, but basically the opposite face (which should be blocked by the front face) is showing through as I rotate it.
I've managed to get rid of the depth buffer error with this:
void MyGLWidget::handleWindowChanged(QQuickWindow *win)
{
if (win) {
connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(cleanup()), Qt::DirectConnection);
win->setClearBeforeRendering(false);
QSurfaceFormat glFormat;
glFormat.setVersion(3,2);
glFormat.setProfile(QSurfaceFormat::CoreProfile);
/*I'm showing everything for context, but this is the key line*/
glFormat.setDepthBufferSize(1);
win->setFormat(glFormat);
}
}
So now I'm only getting the stencil error, but that causes a different issue. One side is completely black, and doesn't show any of the lighting.
Some other background info: I'm displaying a QQuickView. My OpenGLWidget is imported into QML and embedded like so:
MyGLWidget {
id: glWidget
}
In the paint() of my renderer, I am calling glEnable(GL_DEPTH_TEST) and glEnable(GL_STENCIL_TEST) at the top, but that doesn't seem to do anything. Maybe I'm calling that in the wrong context? I don't know where else I would be able to call it, however.
As it turns out, all of my models (including the spider) had incorrect vertex normals, hence the shading issues. I should have checked them out in other rendering software.
glFormat.setDepthBufferSize(1) was the real fix
Try
glDepthMask(GL_TRUE);
// your paintings here
glDepthMask(GL_FALSE);
See also Scene Graph and Rendering.
Related
I render my scene to FBO with multisampling. The resulting texture I use as ImGui::Image content. But how can I disable multisampling for gui?
I tried:
glDisable(GL_MULTISAMPLE);
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL2_NewFrame(Game::getWindow());
ImGui::NewFrame();
//...
// Some gui elements
//...
ImGui::Render();
ImGui::UpdatePlatformWindows();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glEnable(GL_MULTISAMPLE);
But this doesn't work. MSAA actually enabled when the gui drawing(text is blurred).
When it comes to window contexts, you can't disable MSAA after the window has been created. You need to recreate your window for it to take effect.
I'm not sure if the same thing is needed for FBOs, however, to my knowledge calling glDisable(GL_MULTISAMPLE) at runtime only cleans up some AA state, and you can't rely on it, since it's intended that you recreate your window to make changes to MSAA, so I'd guess it probably doesn't affect your FBO the way you expect it to either.
There's also an open issue on the ImGui repo about dealing with AA and fonts, though it seems to be "on hold" for now.
I'd like to show my Qt Quick content on a virtual screen inside my OpenSceneGraph scene.
The approach I'm using right now is highly inefficient:
Render Qt Quick to the offscreen surface using FBO (FrameBufferObject)
Download pixels with QOpenGLFramebufferObject::toImage()
Upload pixels to the OSG
So it's GPU-CPU-GPU transfer. Source code
A proper solution should somehow utilize existing FBO and be able to transfer data solely inside the GPU.
There are two options exist:
Create FBO on the Qt side and use its texture on the OSG side
Create FBO on the OSG side and feed it to the Qt Quick renderer
The Qt part is OK. And I'm completely lost with the OSG. Could anyone provide me with some pointers?
Finally made it.
General idea:
Render QtQuick to texture using FBO - there are some examples over the internet available.
Use this texture inside OpenSceneGraph
The whole thing includes several tricks.
Context Sharing
To perform certain graphics operations, we must initialize OpenGL global state, also known as context.
When a texture is created, context stores it's id. Ids are not globally unique, so when another texture is created within another context, it may get the same id, but with different resource behind it.
If you just pass your texture's id to another renderer (operating within different context), expecting it to show your texture, you end up showing another texture or black screen or crash.
The remedy is context sharing, which effectively means sharing ids.
OpenSceneGraph and Qt abstractions are not compatible, so you need to tell OSG not to use its own context abstraction. This is done by calling setUpViewerAsEmbeddedInWindow
Code:
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;
From now on, the existing QOpenGLContext instance will be used as an OpenGL context for OSG rendering.
You will need to create another context for QtQuick rendering and set them shared:
void Widget::initializeGL()
{
QOpenGLContext* qmlGLContext = new QOpenGLContext(this);
// ...
qmlGLContext->setShareContext(context());
qmlGLContext->create();
}
Remember, there can be only one active context at a time.
Your scheme is:
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
Making of a proper osg::Texture
Since OSG and Qt API are incompatible you barely can link QOpenGLFrameBufferObject to osg::Texture2D as it is.
QOpenGLFrameBufferObject has QOpenGLFrameBufferObject::texture() method which returns opengl texture id, but osg::Texture manages all openGL stuff on its own.
Something like osg::Texture2D(uint textureId); could help us but it just doesn't exist.
Let's make one by ourselves.
osg::Texture is backed by osg::TextureObject which stores OpenGL texture id and some other data as well. If we construct osg::TextureObject with a given texture id and pass it to osg::Texture, the latter will use it as its own.
Code:
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);
}
Complete demo project here
I'm new in Qt and I'm making an app that generates a 3D object with some points the user provides in GUI. The user defines the points and then, in the same window, a QOpenGLWidget paints the final object. But then, the QOpenGLWindow doesn't draw the model (only the GlCLearColor). I've tried the same functions but called before app.exec() in main and it works fine.
I don't know what's happening. I've tried calling makeCurrent() before working with vao and vbo but it doesn't work.
I haven't created any thread neither fbo.
When I tried makeCurrent before working with vao and vbo I got:
"QOpenGLBuffer::bind: buffer is not valid in the current context"
"QOpenGLShaderProgram::bind: program is not valid in the current context."
EDIT I retried making current before vao, vbo and shader binding and that messages dissapeared but the widget isn't painting the object.
I've also tried QOPenGLWidget::update() after drawing. It doesn't draw with resizing.
I'm using OpenGL 4.1 Core Profile and Qt 5.12.3
I've already solved it! I just forgot to make current before setting uniform values in shaders.
Simply put, I cannot for the life of me figure out how to actually make use of things like QOpenGLWidget or QOpenGLWindow or anything. I want to have the rendering I do be a child widget of a window in a MDI, but nothing works.
Here's the code I have currently set up for the widget (at least, just the parts involving OpenGL):
Viewport::Viewport(QWidget * parent) : QOpenGLWidget(parent) { }
void Viewport::initializeGL() {
initializeOpenGLFunctions();
}
void Viewport::paintGL() {
// first, clear the screen
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
And here's how I use the widget:
vp = new Viewport;
vp->resize(QSize(320,240));
hbox->addWidget(vp);
And the result is that I see nothing. I just see a tiny sliver of empty space next to the other widget in the window, but that's it. No black screen like I try to clear with, not even a 320x240-sized empty space.
Like I said, I've been unable to do this in any of the ways I could find, and it's really frustrating. Am I missing something obvious? There's very little documentation as-is, so it's hard to tell if I am, or if there's some weird corner case I'm running into. (For example, none of the documentation I find uses QOpenGLWidget as part of a larger widget; is that because it can't be, or because all the examples I can find are just lazy about using the widget as its own top-level window?)
After some more fiddling around, it turns out my issue was apparently caused by the other object in the window (a QListView) by default taking up as much space as possible, making the OpenGL widget disappear since it doesn't have a minimum size.
In other words, the problem is fixed by either changing the QListView to have a QSizePolicy::Preferred sizing policy (since resizing the window will now let you see the OpenGL widget), or by giving the OpenGL widget a minimum or fixed size.
(As an aside, I really wish this could've been more obvious than just stumbling upon it by chance.)
I'm stumped. I have a widget inside the mainwindow on a QT 4.6 application which has been configured as a openGL widget. It draws just fine except I am unable to clear the background between frames. I get a black background when he window opens and it never clears after that, so I get a jumbled mess of rendered objects as time goes along. I'm also having to call swapBuffers() at the end of the paintGL() function to get the widget to show the most recent frame which has me puzzled as I was under the impression that swapBuffers() was called automatically. I am set up for double buffering and the widget is not shared. Here is the relevant code:
void GLWidget::paintGL ( )
{
m_Input.Draw();
QGLWidget::swapBuffers();
}
void GLWidget::initializeGL ( )
{
qglClearColor(QColor(0,0,255,128));
glClear(GL_COLOR_BUFFER_BIT);
}
It does seem there's something not right with the double buffering. Clearing the screen to a background color is pretty basic. But it's driving me nuts as to why it's not working. The remainder of the drawing code is working fine. Any ideas? (This is on a Linux system.)
glClear is a drawing operation. It doesn't belong into initializeGL but into paintGL. The clearing color should be set right before calling glClear as well, so move that [q]glClearColor along.
Update
The paintGL method should look like this:
void GLWidget::paintGL()
{
qglClearColor(QColor(0,0,255,128));
glClear(GL_COLOR_BUFFER_BIT);
// you probably also want to clear the depth and stencil buffers
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
m_Input.Draw();
QGLWidget::swapBuffers();
}
Of course you must make sure that m_Input.Draw() doesn't mess things up again. You didn't show the code for that, so I'm in the blind here.