OpenGL屏幕外渲染
我有一个可以创建3D模型并从中导出图像的应用程序.我用这个例子来做:
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.
我试图删除glut *功能并运行我的程序,但运行该程序时未导出任何内容.我在Google上搜索了一下,发现我应该使用Framebuffer,但是找不到任何示例.
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.
如何设置渲染3D模型时程序不显示任何窗口?
How can I set my program doesn't show any window when rendering my 3D model?
注意:我想在Windows和Linux中运行该程序.
Note: I want to run this program in Windows and Linux.
我刚刚了解了为Windows编写的源代码.由于这是对生产性代码的研究(因此使用了我们的生产性代码的其他内容),因此我无法按原样提供它.我在这里展示的是一个剥离后的版本,该版本应显示其工作方式:
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;
}
我没有检查它是否可以编译(如上所述).它被剥夺了可以在Windows 10上运行的代码.
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.
关于OpenGL和Windows的说明:
我自己进行了GL绑定,因为Microsoft Windows OpenGL API不支持OpenGL 3.0或更高版本. (我本来可以使用 glfw 之类的库.)这意味着我必须为函数分配函数地址指针(以纠正函数原型),以便我可以使用C函数调用正确地调用OpenGL函数.
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:
-
被调用函数的签名错误. (我使用从khronos.org下载的标头来授予正确的原型.希望驱动程序提供者也可以这样做.)
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.)
该功能在驱动程序中不存在. (我使用的功能是已安装的驱动程序支持的OpenGL标准的一部分.该驱动程序支持OpenGL 4.x,但我仅需要OpenGL 3.x(至少到目前为止).)
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).)
在使用函数指针之前,必须对其进行初始化. (我编写了一个未在代码中公开的初始化.这是我放置注释/* excluded: init our private OpenGL binding as the Microsoft API for OpenGL is stuck <= OpenGL 2.0 */
的地方.
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:
在我的OpenGL初始化函数中,我这样做:
In my OpenGL init function, I do:
glGenFramebuffers
= (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress(
"glGenFramebuffers");
,标题提供:
extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
PFNGLGENFRAMEBUFFERSPROC
由glext.h
提供,我从
typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers);
wglGetProcAddress()
由Microsoft Windows API提供.
wglGetProcAddress()
is provided by the Microsoft Windows API.
有关OpenGL和Linux的说明:
如果硬件和已安装的驱动程序支持所需的OpenGL标准,则功能可以照常使用,
-
包括必要的标头(例如#include <GL/gl.h>)
链接必要的库(例如 -lGL -lGLU
).
derhass评论:
derhass commented:
绝对不能保证GL 3.x函数可以通过任何正在使用的
libGL.so
导出,即使导出了它们,也不能保证该函数受支持(即,mesa使用相同的前端库用于所有驱动程序后端,但每个驱动程序可能仅支持部分功能).您必须在两个平台上都使用扩展机制.
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.
我无法提供有关如何处理此问题的简单建议,我对此也没有宝贵的实践经验.因此,我想至少提供我通过Google搜索找到的这些链接(来自khronos.org):
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: