Search code examples
c++qtopenglfbooff-screen

Minimal OpenGL offscreen rendering using Qt


I am trying to make a simple offscreen renderer to produce a bunch of image files using Qt. There are lots of examples around, though I haven't found any dealing with this sort of one-shot rendering, without a loop and without a visible window.

Plus, Qt provides you with convenient wrappers, which is great, but on the other hand, it becomes harder to follow examples written with C++glew+glfw.

I tried using this to set the offscreen context and it works. After creating an FBO and rendering (like here), no triangle is drawn inside the image (fbo->ToImage).

My current code is just a mix of two:

#include <QtGui/QSurfaceFormat>
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QOpenGLShaderProgram>
#include <QApplication>
#include <QDebug>
#include <QImage>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>

int main(int argc, char* argv[])
{
   QApplication a(argc, argv);

   QSurfaceFormat surfaceFormat;
   surfaceFormat.setMajorVersion(4);
   surfaceFormat.setMinorVersion(3);

   QOpenGLContext openGLContext;
   openGLContext.setFormat(surfaceFormat);
   openGLContext.create();
   if(!openGLContext.isValid()) return -1;

   QOffscreenSurface surface;
   surface.setFormat(surfaceFormat);
   surface.create();
   if(!surface.isValid()) return -2;

   openGLContext.makeCurrent(&surface);

   QSize vpSize = QSize(300, 300);

   qDebug("Hi");



   QOpenGLFramebufferObjectFormat fboFormat;
   fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
   QOpenGLFramebufferObject fbo(vpSize, fboFormat);

   fbo.bind();

   static const float vertexPositions[] = {
       -0.8f, -0.8f, 0.0f,
        0.8f, -0.8f, 0.0f,
        0.0f,  0.8f, 0.0f
   };

   static const float vertexColors[] = {
       1.0f, 0.0f, 0.0f,
       0.0f, 1.0f, 0.0f,
       0.0f, 0.0f, 1.0f
   };

   QOpenGLBuffer vertexPositionBuffer(QOpenGLBuffer::VertexBuffer);
   vertexPositionBuffer.create();
   vertexPositionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
   vertexPositionBuffer.bind();
   vertexPositionBuffer.allocate(vertexPositions, 9 * sizeof(float));

   QOpenGLBuffer vertexColorBuffer(QOpenGLBuffer::VertexBuffer);
   vertexColorBuffer.create();
   vertexColorBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
   vertexColorBuffer.bind();
   vertexColorBuffer.allocate(vertexColors, 9 * sizeof(float));

   QOpenGLShaderProgram program;
   program.addShaderFromSourceCode(QOpenGLShader::Vertex,
                                   "#version 330\r\n"
                                   "in vec3 position;\n"
                                   "in vec3 color;\n"
                                   "out vec3 fragColor;\n"
                                   "void main() {\n"
                                   "    fragColor = color;\n"
                                   "    gl_Position = vec4(position, 1.0);\n"
                                   "}\n"
                                   );
   program.addShaderFromSourceCode(QOpenGLShader::Fragment,
                                   "#version 330\r\n"
                                   "in vec3 fragColor;\n"
                                   "out vec4 color;\n"
                                   "void main() {\n"
                                   "    color = vec4(fragColor, 1.0);\n"
                                   "}\n"
                                   );
   program.link();
   program.bind();

   vertexPositionBuffer.bind();
   program.enableAttributeArray("position");
   program.setAttributeBuffer("position", GL_FLOAT, 0, 3);

   vertexColorBuffer.bind();
   program.enableAttributeArray("color");
   program.setAttributeBuffer("color", GL_FLOAT, 0, 3);

   openGLContext.functions()->glClearColor(0.3f, 0.0f, 0.7f, 1.0f);
   openGLContext.functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   openGLContext.functions()->glDrawArrays(GL_TRIANGLES, 0, 3);


   program.disableAttributeArray("position");
   program.disableAttributeArray("color");

   program.release();

   fbo.release();

   qDebug("FBO released");

   QImage im = fbo.toImage();

   if (im.save("asd.png")){
       qDebug("Image saved!!");
   }



   a.exec();
}

How can I put together the offscreen part with the drawing part?


Solution

  • I suspect the problem is simply that you are getting a 4.3 core profile but aren't creating/using a vertex array object. Creating a VAO immediately after binding the shader program should suffice, so change...

    program.link();
    program.bind();
    

    to...

    program.link();
    program.bind();
    
    QOpenGLVertexArrayObject vao;
    vao.create();
    vao.bind();
    

    (You'll also need to add #include <QOpenGLVertexArrayObject> at the top of your source file.)

    Also note that error checking is important. Consider using glGetError or perhaps a debug context.