I have an app written in Qt5.10 using QOpenGLWidget. I use raw OpenGL calls to draw on the widget.
If I have some viewport area that is drawn with alpha <1.0 this area is not drawn anew, but rather it looks like it's drawn over the previous state of the widget.
When I switch between different Windows apps, it's drawing over previous contents of the screen rendered by a different apps.
I don't change the default update behaviour of the widget.
I set the default SurfaceFormat as follows:
QSurfaceFormat surfaceFormat = QSurfaceFormat::defaultFormat();
surfaceFormat.setRedBufferSize(8);
surfaceFormat.setGreenBufferSize(8);
surfaceFormat.setBlueBufferSize(8);
surfaceFormat.setAlphaBufferSize(8);
surfaceFormat.setStencilBufferSize(8);
surfaceFormat.setDepthBufferSize(16);
surfaceFormat.setSamples(4);
surfaceFormat.setRenderableType(QSurfaceFormat::RenderableType::OpenGL);
surfaceFormat.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
QSurfaceFormat::setDefaultFormat(surfaceFormat);
I was trying to add calls to glClear and glInvalidateFramebuffer in random places in the code, but it didn't seem to help.
What should I do to force redrawing of the widget?
Related
I am working on updating an application for a client.
They use Qt and currently use a QGLWidget to display a full-screen view of 1 of 4 possible cameras selected by clicking the appropriate radio button. They then use OpenGL to draw on the image being displayed. This works great, but they want to update the UI to include a quad-split view of all 4 cameras.
My first thought on how to accomplish this was to keep the one QGLWidget for the full-screen display, and have 4 small QGLWidgets for the quad-split. From the documentation I found that you can't overlap QGLWidgets or QOpenGLWidgets because they don't handle z-order appropriately, but that this can be accomplished by using QOpenGLWindows and QWidget::createWindowContainer.
So, I coded up an application that uses a QOpenGLWidget (trying to bring them up to date) for the full-screen view, and 4 smaller QOpenGLWindows using QWidget::createWindowContainer, but this isn't working either.
The widgets built from QOpenGLWindows are always on top even if I use lower() to try to get them behind the full screen QOpenGLWidget. I've also tried using hide() on the widgets built from QOpenGLWindows, however, this has had no effect.
Do this at a lower level. Keep the one QGLWidget -- in fact don't touch your Qt objects. Instead, change the lower-level rendering so that it makes 4 calls to glViewport.
After each call to glViewport, update the modelview and projection and matrices according to the camera of interest, then draw the 3D scene.
This is simple and performant, because the driver only needs to deal with a single OpenGL context. You might have some extra work to adjust mouse input, but I think it'll be worthwhile.
On screen 0, I have the mainWindow with two QOpenGLWidgets running video. On screen 1, I have another QOpenGLWidget in fullscreen mode running video. The screen 0 is a preview of what the user is seeing on the second display, which is a pair of glasses. Everything was working fine, until I added the fullscreen widget. Now only the fullscreen widget updates and the other two only update if I cause the window to repaint, e.g. moving the window.
I am a noob when it comes to opengl, but the two widgets, in the mainWindow are running together fine. So I don't believe it is the code. Is this a limitation of opengl? Qt? me?
I am using Qt5.4.
I missed an error in the console due to a lot of noise.
Requires a valid current OpenGL context.
Texture has not been destroyed
A quick search and I got my answer. I wasn't calling makeCurrent() when I was updating the texture with the next frame.
I have a game I'm currently working on, and it uses multiple views (for a minimap for example).
Thing is, I would like to have a fading effect added at some point, so I thought I'd create a black image that is the size of the screen and change its alpha value with a timer. That part is not a problem.
What happens right now is the main area (ie window default view) is fading (because the opacity of the image is increasing), but the minimap (minimap view) is unaffected. This is normal behaviour for views, but is there a way to draw an image to the whole window, regardless of the views ?
Thanks in advance
To clarify, you have the default view where you'll draw the main game, then you'll have the minimap view where you would draw the minimap. At some point in the game you want the whole screen to fade to black. It sounds like you've been trying to draw a black image on the default view (changing the alpha) to make this effect work.
So, you need a third view that you draw your black image on to get this fading effect.
I'm currently using the QWidget::grab() function to acquire a QFrame's pixmap (and all of its children), but the function doesn't seem to take into account if the widget doesn't have any background.
You see, my QFrame is set to "setAutoFillBackground(false)", but when its pixmap is grabbed, it seems to paint the default light-pinkish background instead of full transparency.
Replacing the pixmap with a picture containing an alpha channel works fine.
The situation I'm using this in is with QGL, so the pixmap is getting rendered later on as a texture.
I changed the frame's palette's background to contain 0 alpha. This fixed the program.
Although I still believe that the grab function should take into account the bool that was set for filling the background or not - since not autofilling the background equates to the same net visual effect normally, just not when grabbed.
i came about this problem and knew it could be done better.
The problem:
When overlaying a QGLWidget (Qt OpenGL contextview) with Qt widgets, Qt redraws those widgets after every Qt frame.
Qt isn’t built to redraw entire windows with >60fps constantly, so that’s enormously slow.
My idea:
Make Qt use something other to draw upon: a transparent texture. Make OpenGL use this texture whenever it redraws and draw it on top of everything else. Make Qt redirect all interaction with the OpenGL context view to the widgets drawn onto the texture.
The advantage would be that Qt only has to redraw whenever it has to (e.g. a widget is hovered or clicked, or the text cursor in a text field blinks), and can do partial redraws which are faster.
My Question:
How to approach this? how can I tell Qt to draw to a texture? how can i redirect interaction with a widget to another one (e.g. if i move the mouse above the region in the context view where a checkbox sits in the drawn-to-texture widget, Qt should register this event to the checkbox and repaint to reflect itshovered state)
I separate my 2D and 3D rendering out for my CAD-like app for the very same reasons you have, although in my case my the 2D stuff are not widgets - but it shouldn't make a difference. This is how would approach the problem:
When your widget changes render it onto a QGLFramebufferObject, do this by using the FBO as the QPaintDevice for a QPainter in your QGLWidget::paintEvent(..) and calling myWidget->render( myQPainter, ...). Repeat this for however many widgets you have, but only onto the same FBO - don't create an FBO for each one... Remember to clear it first, like a 'normal' framebuffer.
When your current OpenGL background changes, render it onto another QGLFramebufferObject using standard OpenGL calls, in the same way.
Create a pass through vertex shader (the 'camera' will just be a unit cube), and a very simple fragment shader that can layer the two textures on top of each other.
At the end of the QGLWidget::paintEvent(..), activate your shader program, bind your framebuffers as textures for it (myFBO->texture() gets the handle), and render a unit quad. Because your camera is a unit square, and the viewport size defined the FBO size, it will fill the viewport pixel perfect.
However, that's the easy part... The hard part is the widget interaction. Because you are essentially rendering a 'proxy', you going to have to relay the interaction between the 'real' and 'proxy' widget, whilst keeping the 'real' widget invisible. Here's how would I start:
Some operating systems are a bit weird about rendering widgets without ever showing them, so you may have to show and then hide the widget after instantiation - because of the clever painting queue in Qt, it's unlikely to actually make it to the screen.
Catch all mouse events in the viewport, work out which 'proxy' widget the cursor is over (if any), and then offset it to get the relative position for the 'real' hidden widget - this value will depend on what parent object the 'real' widget has, if any. Then pass the event onto the 'real' widget before redrawing the widget framebuffer.
I should state that I also had to create a 'flagging' system to handle redraws nicely. You don't want every widget event to trigger a widget FBO redraw, because there could many simultaneous events (don't just think about the mouse) - but you would only want one redraw. So I created a system where if anything in the application could change anything in the viewport visually, then it would flag the viewport as 'dirty'. Then setup a QTimer for however many fps you are aiming for (in my situation the scene could get very heavy, so I also timed how long a frame took and then used that value +10% as the timer delay for the next check, this way the system isn't bombarded when rendering gets laggy). And then check the dirty status: if it's dirty, redraw; otherwise don't. I found life got easier with two dirty flags, one for the 3D stuff and one for the 2D - but if you need to maintain a constant draw rate for the OpenGL drawing there's probably no need for two.
I imagine what I did wasn't the easiest way to do it, but it provides plenty of scope for tuning and profiling - which makes life easier in the long run. All the answers are definitely not in this post, but hopefully it will get you on the way to a strategy.