OpenGL屏幕外渲染

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenGL屏幕外渲染相关的知识,希望对你有一定的参考价值。

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

#include <windows.h>
#include <GLGL.h>
#include <GLglu.h>
#include <GLglut.h>
#include <opencv2highgui.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. */
}

它运行正确,但是当我运行这个程序时,它会创建一个窗口并显示它然后将其删除。

我试图删除glut *函数并运行我的程序,但它运行时没有导出任何东西。我用谷歌搜索它,发现我应该使用Framebuffer,但我找不到任何例子。

在渲染3D模型时,如何设置程序不显示任何窗口?

注意:我想在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并安装了适当的驱动程序,则授予功能的可用性。 (有可能检查驱动程序是否提供某些功能。)

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

  1. 被调用函数的签名是错误的。 (我使用从khronos.org下载的头文件来授予正确的原型。希望驱动程序提供商也能做到。)
  2. 该功能在驱动程序中不存在。 (我使用的功能是安装驱动程序支持的OpenGL标准的一部分。驱动程序支持OpenGL 4.x但我只需要OpenGL 3.x(至少到现在为止)。)
  3. 在使用它们之前必须初始化函数指针。 (我已经编写了一个未在代码中公开的初始化。这是我放置评论/* excluded: init our private OpenGL binding as the Microsoft API for OpenGL is stuck <= OpenGL 2.0 */的地方。

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

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

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

标题提供:

extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;

PFNGLGENFRAMEBUFFERSPROCglext.h提供,我从kronos.org下载:

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

wglGetProcAddress()由Microsoft Windows API提供。

关于OpenGL和Linux的注意事项:

如果H / W和安装的驱动程序支持所需的OpenGL标准,则可以像往常一样使用功能

  1. 包括必要的标题(例如#include <GL/gl.h>)
  2. 链接必要的库(例如-lGL -lGLU)。

derhass评论说:

绝对不能保证GL 3.x函数是由任何正在使用的libGL.so导出的,即使它们被导出,也不能保证该函数是受支持的(即mesa对所有驱动程序后端使用相同的前端库,但是每个驱动程序可能只支持一部分功能)。您必须在两个平台上使用扩展机制。

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

以上是关于OpenGL屏幕外渲染的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 FBO 的 OpenGL 3.2+ 的 Linux 屏幕外渲染

OpenGL屏幕外渲染

使用 OpenGL C++ 渲染到纹理

离屏帧缓冲区opengl上的glGetPixels

用于片段着色器的 OpenGL GLSL 绑定采样器

为啥在使用 FBO 进行多重采样时,OpenGL 会使我的场景变亮?