0

I have an application that creates a 3D model and exports an image from that. I use this example to do it:

#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. */
}

It does correctly but when I run this program it creates a window and shows it then removes it.

I've tried to remove glut* function and run my program but it didn't export anything when running it. I googled it and found that I should use Framebuffer but I couldn't find any example.

How can I set my program doesn't show any window when rendering my 3D model?

Note: I want to run this program in Windows and Linux.

Onur A.
  • 2,938
  • 3
  • 20
  • 35
Amin Mirzaee
  • 89
  • 10
  • 2
    Did you notice [SO: How to render offscreen on OpenGL?](https://stackoverflow.com/q/12157646/7478597)? – Scheff's Cat Jan 01 '18 at 12:30
  • Yes, but when i call 'glGenFramebuffers(1, &fbo);' my program crash – Amin Mirzaee Jan 01 '18 at 12:36
  • 2
    I tried to solve this a few weeks ago. I didn't find a ready cooking recipe but was able to finally put the pieces together. I solved it for Windows by creating a Window which was needed for [`wglCreateContext()`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd374379(v=vs.85).aspx). However, I did this in a simple console application, and I did not show the window. The part with creating the window and the wgl context was the only non-portable part. (I surrounded it with `#ifdef _WIN32`.) For the non-windows alternative, I planned to use [libegl](https://www.khronos.org/egl). – Scheff's Cat Jan 01 '18 at 12:37
  • I use wglCreateContext and it doesn't save anything. Could you put some esource code? – Amin Mirzaee Jan 01 '18 at 12:47
  • You really need to learn about OpenGL contexts. – derhass Jan 01 '18 at 12:51

1 Answers1

1

I just had a look into the source code I did for Windows. As it was a study for productive code (and hence uses other stuff of our productive code) I cannot provide it as is. What I present here is a stripped version which should show how it works:

// 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;
}

I didn't check whether this even compiles (as is above). It is stripped out of code which compiles and run on Windows 10 on my side.


A Note about OpenGL and Windows:

I did the GL binding by myself because the Microsoft Windows OpenGL API does not support OpenGL 3.0 or higher. (I could've used a library like glfw instead.) This means I have to assign function addresses to function pointers (to correct function prototypes) so that I can call OpenGL functions properly using C function calls.

The availability of the functions is granted if I have appropriate H/W and the appropriate drivers installed. (There are possibilities to check whether the driver provides certain functions.)

If such bound function call fails (e.g. with a segmentation fault) possible reasons could be:

  1. The signature of called function is wrong. (I used headers downloaded from khronos.org to grant correct prototypes. Hopefully, the driver provider did as well.)

  2. The function does not exist in the driver. (I use functions which are part of the OpenGL standard which is supported by the installed driver. The driver supports OpenGL 4.x but I need only OpenGL 3.x (at least until now).)

  3. The function pointers have to be initialized before I use them. (I have written an initialization which is not exposed in the code. This is where I placed the comment /* excluded: init our private OpenGL binding as the Microsoft API for OpenGL is stuck <= OpenGL 2.0 */.

To illustrate this, some code examples:

In my OpenGL init function, I do:

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

and the header provides:

extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;

PFNGLGENFRAMEBUFFERSPROC is provided by glext.h I downloaded from kronos.org:

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

wglGetProcAddress() is provided by the Microsoft Windows API.

A Note about OpenGL and Linux:

If H/W and the installed driver supports the desired OpenGL standard, functions can be used as usual by

  1. including the necessary headers (e.g. #include <GL/gl.h>)

  2. linking the necessary libraries (e.g. -lGL -lGLU).


derhass commented:

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.

I'm not able to provide a simple recommendation how to handle this nor I've valuable practical experience about this. So, I want to provide at least these links (from khronos.org) I've found by google search:

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • I extended my answer concerning "when i call 'glGenFramebuffers(1, &fbo);' my program crash". – Scheff's Cat Jan 01 '18 at 16:14
  • Your advice for i linux is very problematic. 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. The only platform which actually guarantees GL3.x entrypoints without extension mechanism is MaxOS X (and that only for core profiles). – derhass Jan 01 '18 at 16:17
  • 1
    @derhass Hmm. The only non-Windows platforms, I used OpenGL on in the past were SGI Irix (excellent OpenGL support as they invented it), PC Linux with NVidia H/W and drivers (probably best available support for PC/Linux). We could do so as we sold our S/W with appropriate H/W or gave clear recommendations to our customers which H/W to prefer - a comfortable solution to prevent such problems. Hence, I've very limited knowledge about possible issues on non-Windows. (We never had any.) So, could you help to improve the answer a bit concerning Linux? – Scheff's Cat Jan 01 '18 at 16:24
  • 1
    Well, if you want to use OpenGL > 1.1 in a cross-platform way, you _must_ use the [extension mechanism](https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions). My general advise would be to use [an extension loader](https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library) on every platform as a fire-and-forget solution (I prefer [glad](https://github.com/Dav1dde/glad) which as a code generator does not add any runtime dependcies). – derhass Jan 01 '18 at 16:56
  • @derhass I just realized that [glfw](http://www.glfw.org/) announces Linux and Mac OS/X support as well. (They also provide a link to [alternatives](https://www.khronos.org/opengl/wiki/Related_toolkits_and_APIs).) Concerning glad, I found the overview [OpenGL Loading Library](https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library) where others are mentioned as well. – Scheff's Cat Jan 01 '18 at 17:07