将 OpenGL 渲染保存到图像文件

can*_*din 4 c++ opengl qt fbo render-to-texture

尽管 OpenGL 的功能很简单,但仍然让我感到困惑,但我开始学习它是如何工作的。

我正在寻找一个离屏渲染的最小示例来帮助我入门。

我的应用程序将获取一堆三角形以及有关如何相对于相机定位它们的信息,并将渲染结果保存到图像文件中。目前没有照明、材质或后期处理。

我观看了有关创建离屏上下文、创建 FBO、渲染到纹理等的教程。我不介意使用 QT,因为它方便地提供 OpenGL 工具、窗口和 QImage。据我了解,为了能够对渲染图像进行图像处理,您需要将渲染目标设置为纹理,然后使用着色器,最后将纹理读取到数组中。

试图把事情放在一起从来没有让我有一个好的起点。我要么陷入设置依赖项的困境,要么黑屏,要么盯着那些除了我需要的东西之外做了太多事情的项目。

更新1:可以正常工作了。

#include <QtGui/QGuiApplication>
#include <QtGui/QSurfaceFormat>
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLFunctions_4_3_Core>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QOpenGLShaderProgram>
#include <QDebug>
#include <QImage>
#include <QOpenGLBuffer>

int main(int argc, char* argv[])
{
   QGuiApplication 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);

   QOpenGLFunctions_4_3_Core f;
   if(!f.initializeOpenGLFunctions()) return -3;

   qDebug() << QString::fromLatin1((const char*)f.glGetString(GL_VERSION));

   QSize vpSize = QSize(100, 200);

   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\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\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);

   f.glClearColor(0.3f, 0.0f, 0.7f, 1.0f);
   f.glClear(GL_COLOR_BUFFER_BIT);

   f.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!!");
   }

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

保存的图像与FBO具有相同的大小,颜色对应于glClearColor中设置的颜色,但不渲染三角形。我缺少什么?

Sur*_*ine 8

glReadPixels渲染和交换后可以使用:

int* buffer = new int[ WIDTH * HEIGHT * 3 ];
...
glReadPixels( 0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, buffer );
Run Code Online (Sandbox Code Playgroud)

然后你可以将其buffer写入 .tga 文件:

FILE   *out = fopen(tga_file, "w");
short  TGAhead[] = {0, 2, 0, 0, 0, 0, WIDTH, HEIGHT, 24};
fwrite(&TGAhead, sizeof(TGAhead), 1, out);
fwrite(buffer, 3 * WIDTH * HEIGHT, 1, out);
fclose(out);
Run Code Online (Sandbox Code Playgroud)