QOpenGLFramebufferObject provides methods that internally create a texture and attach it to the FBO. But what if I have an existing texture ID (taken from a QSGTexture btw) and I want to attach it to the FBO?
Related
I'm trying to incorporate external Vulkan render commands inside the Qt Quick scenegraph following the example here. I would like to get access to current swap chain image and issue draw commands on it. QVulkanWindow has QVulkanWindow::currentSwapChainImageIndex() and QVulkanWindow::swapChainImage(int index), however I don't know how to get these from a QQuickWindow. QQuickWindow has an associated QSGRendererInterface that can be used as follows:
// m_window is a QQuickWindow*
auto instance = m_window->vulkanInstance();
QSGRendererInterface *rif = m_window->rendererInterface();
auto physicalDevice = *reinterpret_cast<VkPhysicalDevice*>(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource));
Is there a way to query the current swap chain image for a QQuickWindow?
QQuickWindow::renderTargetId() is removed in Qt 6.0, because underlying graphics API now may be different from OpenGL.
There is now QQuickRenderTarget which is abstract from graphics API, but I can't obtain FBO ID from it when I use OpenGL (I call QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL) in main()).
So I render my scene in my offscreen FBO, but when I want to render a result into the FBO, created by a QQuickWindow, which is not the default FBO with ID = 0 (QQuickWindow creates an FBO with ID = 1 right now), because my QQuickWindow is used by a QQuickWidget embedded into a QMainWindow (so I put Qt Quick and QML into a QWidget-based UI).
I may query the current set FBO ID via OpenGL API, of course, before I call glBindFramebuffer() to render into my own FBO, but I would like to read QQuickWindow's FBO ID using it's API, is there any way?
My custom QQuickItem currently does the following
Create a QSGNode that subclasses QSGSimpleTextureNode
In the nodes preprocess function, create a QOpenGLFramebufferObject to draw to
Draw on the QOpenGLFramebufferObject using a QPainter
Display the contents of the QOpenGLFramebufferObject as the node contents
The process I have for converting the FBO to a QSGTexture that I can set on the QSGSimpleTextureNode is the following.
QImage img = m_fbo->toImage();
QSGTexture* tex = m_window->createTextureFromImage(img, QQuickWindow::TextureCanUseAtlas);
setTexture(tex);
This seems very inefficient, and the app starts to get real framey even with relatively reasonable sized FBOs.
My questions are the following.
Is there a simpler way of getting an FBO into a QSGTexture?
Is there a better QPaintDevice compatible item that I should be using rather than a QOpenGLFramebufferObject?
Is there a better subclass I should be extending than QSGSimpleTextureNode to do what I am wanting to do?
Thanks!
1) For non multisample framebuffer objects a texture with the specified texture target is created. You can get the texture id for the texture attached to framebuffer object, using QOpenGLFramebufferObject::takeTexture(). And then
create a new QSGTexture object from an existing GL texture id:
QSize textureSize = m_fbo.size();
GLuint textureId = m_fbo.takeTexture();
QSGTexture* texture = window()->createTextureFromId(textureId, textureSize);
2, 3) The QQuickPaintedItem class provides a way to use the QPainter API in the QML Scene Graph.
The QQuickFramebufferObject class is a convenience class for integrating rendering using a framebuffer object (FBO) with Qt Quick.
I try to code an OpenGL project with Qt (v5.1.1) on OS X 10.9, in the manner of the modern pipeline implementation. The program is supposed to be a multi-agent based system or particle system. However I lack in understanding how to draw something out of another class.
In cinder there were some simple drawThisAndThat() command you could call. I read the 6th edition of the 'OpenGL Superbible'. From this and several tutorials all examples seem to cover just programs where all modifications are made out of the class that initializes OpenGL.
I would like to instantiate some objects moving on a grid and draw pixel to display their position. I know I have to call void glVertexAttrib4fv(GLuint index, const GLfloat * vi); but this is not sufficient.
Do I need to call glEnableVertexAttribArray(1); and glDrawArrays(GL_POINTS, 0, 3); as well and what else?
Am I right, to instantiate the class controlling the particles after instantiating OpenGL and bevor the main loop?
How do I manage that the particle draws himself while erasing the position he was drawn bevor?
The program is based on this code.
To answer your questions completely I would have to write a wall of text, I will try to only point out the most important aspects. I hope this will help you enough to use your knowledge and probably further reading to get it to work.
all modifications are made out of the class that initializes OpenGL
You can encapsulate update(time) and draw() methods for your Objects which you then call in your main loop.
Do I need to call glEnableVertexAttribArray(1); and glDrawArrays(GL_POINTS, 0, 3); as well and what else?
I would put all particles into one vertex array to avoid rebinding of different vertex arrays after each particle. Then you would have to use glBindVertexArray(vaid); and glDrawArrays(GL_POINTS, 0, vertexCount); in your draw() call. Be careful with vertexCount, it's not the number of floats (as your question implies) but the number of vertices, which should be 1 in your example or the number of particles in my suggested approach (If I'm correct in assuming that the 3 stands for "x, y, and z of my vertex").
And since you only have particles glDrawElements(...); would probably already fit your needs.
Am I right, to instantiate the class controlling the particles after instantiating OpenGL and bevor the main loop?
Probably your instantiation order is correct that way. You definitely should do all instantiations before calling the main loop in your case.
How do I manage that the particle draws himself while erasing the position he was drawn bevor?
If understand your last question correctly: Simply by changing the elements in your buffer objects (glBufferData(...);). Since you will clear the screen and swap buffers after each loop this will make them move. Just update their position with an update(time) call, e.g. pos = pos + dir * time;, put the new positions into a buffer and push that buffer with glBufferData(...) to the vertex array. Remember to bind the vertex array before pushing the buffer.
Some additional things I'd like to point out.
glEnableVertexAttribArray(1); is to enable a vertex attribute in your shader program to be able to pass data to that attribute. You should create a shader program
id = glCreateProgram()
// ... create and attach shaders here
// then bind attribute locations, e.g. positionMC
glBindAttribLocation(id, 0, "positionMC");
glLinkProgram(id);
And after initializing the vertex array with glGenVertexArrays(); you should enable all attributes your vertex array needs in your shader program. In this example positionMC would be at location 0, so you would call something like
glUseProgram(pid);
glBindVertexArray(vaid);
glEnableVertexAttribArray(1);
glVertexAttribPointer(...);
This has only to be done once, since OpenGL stores the state for every particular vertex array. By rebinding a vertex array you will restore that state.
In the main loop all you have to do now is calling your update and draw methods, e.g.:
handleInputs();
update(deltaTime);
glClear(...);
draw();
swapBuffers();
I want to implment push_group/pop_group of cairo with QPainter, but QPainter resets all its state while begin() with a new painterDevice, so I have to save/revert all state manually.
Yes, just check out QPainter::save() and QPainter::restore().
If you want to save/restore between the lifespan of multiple QPainters, you have to do it manually. You could just create a class PainterState that encapsulates the painter state (pen, brush, transform, etc.), and then store a QStack<PainterState>.
There is a QPainterState class, but it is for internal use only, and I think it's only for use with a single QPainter. See the source ("qpainter_p.h") if you're interested in the QPainterState members (too many to copy here).
When constructing the QPainter object, you can draw it to a QPicture. Then it can be reloaded when needed and painted out to the real QPaintDevice.
QPicture picture;
QPainter painterQueued;
painterQueued.begin(&picture); // paint in picture
painterQueued.drawEllipse(10,20, 80,70); // draw an ellipse
painterQueued.end(); // painting done
QImage myImage;
QPainter painterTarget;
painterTarget.begin(&myImage); // paint in myImage
painterTarget.drawPicture(0, 0, picture); // draw the picture at (0,0)
painterTarget.end(); // painting done
You could queue up many QPicture objects in a list, stack, etc, and replay them when needed.