简体   繁体   English

将 OpenGL 渲染保存到图像文件

[英]Save OpenGL rendering to an image file

Although simple for what it does OpenGL is still confusing to me, I am beginning to learn how it works.尽管 OpenGL 的作用很简单,但我仍然感到困惑,但我开始了解它的工作原理。

I am looking for a minimal example of off-screen rendering to get me started.我正在寻找一个最小的屏幕外渲染示例来帮助我入门。

My application shall take a bunch of triangles and information about how to position them relative to the camera and save the rendered result to an image file.我的应用程序将获取一堆三角形和有关如何相对于相机定位它们的信息,并将渲染结果保存到图像文件中。 No lighting, materials or post processing for now.目前没有照明、材料或后期处理。

I watched tutorials about creating off-screen contexts, creating an FBO, render to texture etc. I don't mind using QT as it conveniently provides OpenGL tools, windows and QImage.我看过有关创建屏幕外上下文、创建 FBO、渲染到纹理等的教程。我不介意使用 QT,因为它方便地提供了 OpenGL 工具、窗口和 QImage。 From what I understand, in order to be able to do image processing on the rendered image you need to set up your render target to be a texture, then use shaders and finally read the texture to an array.据我了解,为了能够对渲染图像进行图像处理,您需要将渲染目标设置为纹理,然后使用着色器,最后将纹理读取到数组中。

Trying to put things together never landed me to a good starting point.试图把事情放在一起从来没有让我找到一个好的起点。 I either get stuck with setting dependencies, getting black screen or gaze at projects that do too many things aside for what I need.我要么被困在设置依赖项上,要么黑屏,要么盯着那些除了我需要的东西做太多事情的项目。

Update 1: got it sort of working.更新 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;
}

The saved image has the same size as the FBO, the color corresponds to the one set in glClearColor but the triangle is not rendered.保存的图像与 FBO 大小相同,颜色对应于 glClearColor 中设置的颜色,但不渲染三角形。 What am I missing?我错过了什么?

You can use glReadPixels after rendering and swapping:您可以在渲染和交换后使用glReadPixels

int* buffer = new int[ WIDTH * HEIGHT * 3 ];
...
glReadPixels( 0, 0, WIDTH, HEIGHT, GL_BGR, GL_UNSIGNED_BYTE, buffer );

Then you can write the buffer into a .tga file:然后您可以将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);

A little late.有一点晚。

As a FBO is being used the following code added just before the fbo->release() will save the image as a PNG.由于正在使用 FBO,因此在fbo->release()将图像保存为 PNG 之前添加了以下代码。

QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
fb.save("/path_to_image_file/image_file.png", "PNG");

If you want to make sure all the rendering is complete then you could do the following before:如果您想确保所有渲染完成,那么您可以在此之前执行以下操作:

QOpenGLContext::currentContext()->functions()->glFlush();

You might also want to add the following to the fbo format:您可能还想将以下内容添加到 fbo 格式中:

fboFormat.setTextureTarget(GL_TEXTURE_2D);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM