OpenGL 离屏渲染

OpenGL offscreen render

我有一个创建 3D 模型并从中导出图像的应用程序。我用这个例子来做:

#include <windows.h>
#include <GL\GL.h>
#include <GL\glu.h>
#include <GL\glut.h>
#include <opencv2\highgui.hpp>

GLfloat light_diffuse[] = { 1.0, 0.0, 0.0, 1.0 };  /* Red diffuse light. */
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };  /* Infinite light location. */
GLfloat n[6][3] = {  /* Normals for the 6 faces of a cube. */
    { -1.0, 0.0, 0.0 },{ 0.0, 1.0, 0.0 },{ 1.0, 0.0, 0.0 },
    { 0.0, -1.0, 0.0 },{ 0.0, 0.0, 1.0 },{ 0.0, 0.0, -1.0 } };
GLint faces[6][4] = {  /* Vertex indices for the 6 faces of a cube. */
    { 0, 1, 2, 3 },{ 3, 2, 6, 7 },{ 7, 6, 5, 4 },
    { 4, 5, 1, 0 },{ 5, 6, 2, 1 },{ 7, 4, 0, 3 } };
GLfloat v[8][3];  /* Will be filled in with X,Y,Z vertexes. */

void drawBox(void)
{
    int i;

    for (i = 0; i < 6; i++) {
        glBegin(GL_QUADS);
        glNormal3fv(&n[i][0]);
        glVertex3fv(&v[faces[i][0]][0]);
        glVertex3fv(&v[faces[i][1]][0]);
        glVertex3fv(&v[faces[i][2]][0]);
        glVertex3fv(&v[faces[i][3]][0]);
        glEnd();
    }
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawBox();
    glFlush();
}

void init(void)
{
    /* Setup cube vertex data. */
    v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
    v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
    v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
    v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
    v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
    v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;

    /* Enable a single OpenGL light. */
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    /* Use depth buffering for hidden surface elimination. */
    glEnable(GL_DEPTH_TEST);

    /* Setup the view of the cube. */
    glMatrixMode(GL_PROJECTION);
    gluPerspective( /* field of view in degree */ 40.0,
        /* aspect ratio */ 1.0,
        /* Z near */ 1.0, /* Z far */ 10.0);
    glMatrixMode(GL_MODELVIEW);
    gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
        0.0, 0.0, 0.0,      /* center is at (0,0,0) */
        0.0, 1.0, 0.);      /* up is in positive Y direction */

                            /* Adjust cube position to be asthetic angle. */
    glTranslatef(0.0, 0.0, -1.0);
    glRotatef(60, 1.0, 0.0, 0.0);
    glRotatef(-20, 0.0, 0.0, 1.0);
}

int main(int argc, char **argv)
{
    int width = 500, height = 500;

    /********* i want to remove this section ************/
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutCreateWindow("red 3D lighted cube");
    /********* i want to remove this section ************/

    init();
    display();

    BYTE* result = new BYTE[3 * width *height];
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, result);
    cv::Mat img(width, height, CV_8UC3);
    img.data = result;
    cv::flip(img, img, 0);
    cv::imwrite("D:\result_off.jpg", img);

    return 0;             /* ANSI C requires main to return int. */
}

它没有问题,但是当我 运行 这个程序时,它会创建一个 window 并显示它,然后将其删除。

我试图删除 glut* 函数和 运行 我的程序,但是当 运行ning 它时它没有导出任何东西。我在谷歌上搜索了一下,发现我应该使用 Framebuffer,但找不到任何示例。

如何设置我的程序在渲染我的 3D 模型时不显示任何内容?window?

注意:我想运行这个程序在Windows和Linux。

我刚刚查看了我为 Windows 所做的源代码。由于这是对生产代码的研究(因此使用了我们生产代码的其他内容),因此我无法按原样提供。我在这里展示的是一个精简版,它应该展示它是如何工作的:

// standard C/C++ header:
#include <iostream>

// Windows header:
#include <Windows.h>

using namespace std;

int main(int argc, char **argv)
{
  if (argc < 3) {
    cerr << "USAGE: " << argv[0]
      << " FILE [FILES...] IMG_FILE" << endl;
    return -1;
  }
  // Import Scene Graph
  // excluded: initialize importers
  // excluded: import 3d files
#ifdef _WIN32
  // Window Setup
  // set window properties
  enum { Width = 1024, Height = 768 };
  WNDCLASSEX wndClass; memset(&wndClass, 0, sizeof wndClass);
  wndClass.cbSize = sizeof(WNDCLASSEX);
  wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
  wndClass.lpfnWndProc = &DefWindowProc;
  wndClass.cbClsExtra = 0;
  wndClass.cbWndExtra = 0;
  wndClass.hInstance = 0;
  wndClass.hIcon = 0;
  wndClass.hCursor = LoadCursor(0, IDC_ARROW);
  wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  wndClass.lpszMenuName = 0;
  wndClass.lpszClassName = "WndClass";
  wndClass.hIconSm = 0;
  RegisterClassEx(&wndClass);
  // style the window and remove the caption bar (WS_POPUP)
  DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;
  // Create the window. Position and size it.
  HWND hwnd = CreateWindowEx(0,
    "WndClass",
    "",
    style,
    CW_USEDEFAULT, CW_USEDEFAULT, Width, Height,
    0, 0, 0, 0);
  HDC hdc = GetDC(hwnd);
  // Windows OpenGL Setup
  PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof pfd);
  pfd.nSize = sizeof(pfd);
  pfd.nVersion = 1;
  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = 32;
  pfd.cDepthBits = 16;
  pfd.cStencilBits = 8;
  pfd.iLayerType = PFD_MAIN_PLANE;
  // get the best available match of pixel format for the device context
  int iPixelFormat = ChoosePixelFormat(hdc, &pfd);
  // make that the pixel format of the device context 
  SetPixelFormat(hdc, iPixelFormat, &pfd);
  // create the context
  HGLRC hGLRC = wglCreateContext(hdc);
  wglMakeCurrent(hdc, hGLRC);
#endif // _WIN32
  // OpenGL Rendering Setup
  /* excluded: init our private OpenGL binding as
   * the Microsoft API for OpenGL is stuck <= OpenGL 2.0
   */
  // create Render Buffer Object (RBO) for colors
  GLuint rboColor = 0;
  glGenRenderbuffers(1, &rboColor);
  glBindRenderbuffer(GL_RENDERBUFFER, rboColor);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, Width, Height);
  glBindRenderbuffer(GL_RENDERBUFFER, 0);
  // create Render Buffer Object (RBO) for depth
  GLuint rboDepth = 0;
  glGenRenderbuffers(1, &rboDepth);
  glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, Width, Height);
  glBindRenderbuffer(GL_RENDERBUFFER, 0);
  // create Frame Buffer Object (FBO)
  GLuint fbo = 0;
  glGenFramebuffers(1, &fbo);
  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
  // attach RBO to FBO
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
    GL_RENDERBUFFER, rboColor);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
    GL_RENDERBUFFER, rboDepth);
  // GL Rendering Setup
  // excluded: prepare our GL renderer
  glViewport(0, 0, Width, Height);
  glClearColor(0.525f, 0.733f, 0.851f, 1.0f);
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
  /* compute projection matrix from
   * - field of view (property fov)
   * - aspect ratio of view
   * - near/far clip distance (properties dNear and dFar).
   */
  const DegreeD fov(30.0);
  const double dNear = 0.1, dFar = 100.0;
  const double ar = (float)Width / Height;
  const double d = ::tan(fov / 2.0) * 2.0 * dNear;
  // excluded: construct a projection matrix for perspective view
  // excluded: determine bounding sphere of 3D scene
  // excluded: compute camera and view matrix from the bounding sphere of scene
  // excluded: OpenGL rendering of 3d scene
  // read image from render buffer
  // excluded: prepare image object to store read-back
  //Image::Object img(4, Image::BottomToTop);
  //img.set(Width, Height, Image::RGB24);
  //const size_t bytesPerLine = (3 * Width * 4 + 3) / 4;
  //glReadPixels(0, 0, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, img.getData());
  // store image
  const string filePath = argv[argc - 1];
  // excluded: export image in a supported image file format
  // clean-up
  // excluded: clean-up of 3D scene (incl. OpenGL rendering add-ons)
  glDeleteFramebuffers(1, &fbo);
  glDeleteRenderbuffers(1, &rboColor);
  glDeleteRenderbuffers(1, &rboDepth);
#ifdef _WIN32
  wglMakeCurrent(NULL, NULL);
  wglDeleteContext(hGLRC);
#endif // _WIN32
  // done
  return 0;
}

我没有检查这是否编译(如上所述)。它从我这边 Windows 10 上编译和 运行 的代码中剥离出来。


关于 OpenGL 和 Windows 的注释:

我自己做了 GL 绑定,因为 Microsoft Windows OpenGL API 不支持 OpenGL 3.0 或更高版本。 (我本可以改用 glfw 这样的库。)这意味着我必须将函数地址分配给函数指针(以更正函数原型),以便我可以使用 C 函数调用正确调用 OpenGL 函数。

功能的可用性被授予 如果 我安装了合适的 H/W 和合适的 driver。 (有可能检查 driver 是否提供某些功能。)

如果此类绑定函数调用失败(例如出现分段错误),可能的原因可能是:

  1. 被调用函数的签名错误。 (我使用从 khronos.org 下载的 headers 来授予正确的原型。希望 driver 提供者也能做到。)

  2. driver中不存在该函数。 (我使用的函数是已安装的 driver 支持的 OpenGL 标准的一部分。driver 支持 OpenGL 4.x 但我只需要 OpenGL 3.x (至少直到现在)。)

  3. 函数指针必须在我使用它们之前进行初始化。 (我写了一个没有在代码中公开的初始化。这是我放置注释 /* excluded: init our private OpenGL binding as the Microsoft API for OpenGL is stuck <= OpenGL 2.0 */.

  4. 的地方

为了说明这一点,一些代码示例:

在我的 OpenGL 初始化函数中,我这样做:

  glGenFramebuffers
    = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress(
      "glGenFramebuffers");

并且 header 提供:

extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;

PFNGLGENFRAMEBUFFERSPROCglext.h提供 我从kronos.org:

下载
typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers);

wglGetProcAddress() 由 Microsoft Windows API.

提供

关于 OpenGL 和 Linux 的注意事项:

如果H/W和安装的driver支持所需的OpenGL标准,

可以照常使用功能
  1. 包括必要的headers(例如#include <GL/gl.h>)

  2. 链接必要的库(例如-lGL -lGLU)。


德哈斯评论道:

There is absolutely no guarantee that GL 3.x functions are exported by whatever libGL.so one is using, and even if they are exported, there is no guarantee that the function is supported (i.e. mesa uses the same frontend lib for all driver backends, but each driver may only support a subset of the functions). You have to use the extension mechanism on both platforms.

我无法提供如何处理此问题的简单建议,也无法提供宝贵的实践经验。所以,我想至少提供这些链接(来自 khronos.org)我通过 google 搜索找到: