我能找到的每个介绍和示例似乎都使用 GLUT 或其他一些框架来“初始化”OpenGL。有没有办法用 GL 和 GLU 中可用的东西来初始化 OpenGL?如果不是,那么没有它,GLUT 做什么是不可能的?
7 回答
正如 luke 所指出的,创建和绑定上下文的代码特定于每个窗口平台。
这里有一些函数可以帮助您在特定平台上初始化 OpenGL:
Windows(这里有教程)
- wglCreateContext(hDC)
Mac OS X -- OS X 本质上具有三个选项:Carbon、Cocoa 和底层核心图形层
- Mac OS X OpenGL 开发的完整指南
- 碳:aglCreateContext
- Cocoa:创建(或子类化)一个 NSOpenGLView。他们创建自己的上下文,您可以阅读他们的方法文档以了解如何使其成为最新的。
- CoreGraphicsLayer:CGLCreateContext
Linux
- glx : glXCreateContext
GLX最小可运行示例

改编自这里。
还处理键盘输入。
编译:
gcc glx.c -lGLU -lGL -lX11
在 Ubuntu 14.04 中测试:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#define GL_GLEXT_PROTOTYPES
#define GLX_GLXEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
struct MyWin {
Display *display;
Window win;
int displayed;
int width;
int height;
};
const int WIN_XPOS = 256;
const int WIN_YPOS = 64;
const int WIN_XRES = 320;
const int WIN_YRES = 320;
const int NUM_SAMPLES = 4;
struct MyWin Win;
double elapsedMsec(const struct timeval *start, const struct timeval *stop) {
return ((stop->tv_sec - start->tv_sec ) * 1000.0 +
(stop->tv_usec - start->tv_usec) / 1000.0);
}
void displayCB() {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glVertex3f( 1.0f, -1.0f, 0.0f);
glEnd();
glFlush();
glXSwapBuffers(Win.display, Win.win);
}
void keyboardCB(KeySym sym, unsigned char key, int x, int y,
int *setting_change) {
switch (tolower(key)) {
case 27:
exit(EXIT_SUCCESS);
break;
case 'k':
printf("You hit the 'k' key\n");
break;
case 0:
switch (sym) {
case XK_Left :
printf("You hit the Left Arrow key\n");
break;
case XK_Right :
printf("You hit the Right Arrow key\n");
break;
}
break;
}
}
void reshapeCB(int width, int height) {
Win.width = width;
Win.height = height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
glMatrixMode(GL_MODELVIEW);
}
/* Try to find a framebuffer config that matches
* the specified pixel requirements.
*/
GLXFBConfig chooseFBConfig(Display *display, int screen) {
static const int Visual_attribs[] = {
GLX_X_RENDERABLE , True,
GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT,
GLX_RENDER_TYPE , GLX_RGBA_BIT,
GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR,
GLX_RED_SIZE , 8,
GLX_GREEN_SIZE , 8,
GLX_BLUE_SIZE , 8,
GLX_ALPHA_SIZE , 8,
GLX_DEPTH_SIZE , 24,
GLX_STENCIL_SIZE , 8,
GLX_DOUBLEBUFFER , True,
GLX_SAMPLE_BUFFERS, 1,
GLX_SAMPLES , 4,
None
};
int attribs [ 100 ] ;
memcpy(attribs, Visual_attribs, sizeof(Visual_attribs));
GLXFBConfig ret = 0;
int fbcount;
GLXFBConfig *fbc = glXChooseFBConfig(display, screen, attribs, &fbcount);
if (fbc) {
if (fbcount >= 1)
ret = fbc[0];
XFree(fbc);
}
return ret;
}
GLXContext createContext(Display *display, int screen,
GLXFBConfig fbconfig, XVisualInfo *visinfo, Window window) {
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*,
GLXFBConfig, GLXContext, int, const int*);
/* Verify GL driver supports glXCreateContextAttribsARB() */
/* Create an old-style GLX context first, to get the correct function ptr. */
glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
GLXContext ctx_old = glXCreateContext(display, visinfo, 0, True);
if (!ctx_old) {
printf("Could not even allocate an old-style GL context!\n");
exit(EXIT_FAILURE);
}
glXMakeCurrent (display, window, ctx_old) ;
/* Verify that GLX implementation supports the new context create call */
if (strstr(glXQueryExtensionsString(display, screen),
"GLX_ARB_create_context") != 0)
glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
glXGetProcAddress((const GLubyte *) "glXCreateContextAttribsARB");
if (!glXCreateContextAttribsARB) {
printf("Can't create new-style GL context\n");
exit(EXIT_FAILURE);
}
/* Got the pointer. Nuke old context. */
glXMakeCurrent(display, None, 0);
glXDestroyContext(display, ctx_old);
/* Try to allocate a GL 4.2 COMPATIBILITY context */
static int Context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
/*GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, */
/*GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, */
/*GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_DEBUG_BIT_ARB, */
None
};
GLXContext context = glXCreateContextAttribsARB(display, fbconfig, 0,
True, Context_attribs);
/* Forcably wait on any resulting X errors */
XSync(display, False);
if (!context) {
printf("Failed to allocate a GL 4.2 context\n");
exit(EXIT_FAILURE);
}
printf("Created GL 4.2 context\n");
return context;
}
void createWindow() {
/* Init X and GLX */
Win.displayed = 0;
Display *display = Win.display = XOpenDisplay(":0.0");
if (!display)
printf("Cannot open X display\n");
int screen = DefaultScreen(display);
Window root_win = RootWindow(display, screen);
if (!glXQueryExtension(display, 0, 0))
printf("X Server doesn't support GLX extension\n");
/* Pick an FBconfig and visual */
GLXFBConfig fbconfig = chooseFBConfig(display, screen);
if (!fbconfig) {
printf("Failed to get GLXFBConfig\n");
exit(EXIT_FAILURE);
}
XVisualInfo *visinfo = glXGetVisualFromFBConfig(display, fbconfig);
if (!visinfo) {
printf("Failed to get XVisualInfo\n");
exit(EXIT_FAILURE);
}
printf("X Visual ID = 0x%.2x\n", (int)visinfo->visualid);
/* Create the X window */
XSetWindowAttributes winAttr ;
winAttr.event_mask = StructureNotifyMask | KeyPressMask ;
winAttr.background_pixmap = None ;
winAttr.background_pixel = 0 ;
winAttr.border_pixel = 0 ;
winAttr.colormap = XCreateColormap(display, root_win,
visinfo->visual, AllocNone);
unsigned int mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;
Window win = Win.win = XCreateWindow (display, root_win,
WIN_XPOS, WIN_YPOS,
WIN_XRES, WIN_YRES, 0,
visinfo->depth, InputOutput,
visinfo->visual, mask, &winAttr) ;
XStoreName(Win.display, win, "My GLX Window");
/* Create an OpenGL context and attach it to our X window */
GLXContext context = createContext(display, screen, fbconfig, visinfo, win);
if (! glXMakeCurrent(display, win, context))
printf("glXMakeCurrent failed.\n");
if (! glXIsDirect (display, glXGetCurrentContext()))
printf("Indirect GLX rendering context obtained\n");
/* Display the window */
XMapWindow(display, win);
if (! glXMakeCurrent(display, win, context))
printf("glXMakeCurrent failed.\n");
printf("Window Size = %d x %d\n", WIN_XRES, WIN_YRES);
printf("Window Samples = %d\n", NUM_SAMPLES);
}
void processXEvents(Atom wm_protocols, Atom wm_delete_window) {
int setting_change = 0;
while (XEventsQueued(Win.display, QueuedAfterFlush)) {
XEvent event;
XNextEvent(Win.display, &event);
if(event.xany.window != Win.win)
continue;
switch (event.type) {
case MapNotify:
{
Win.displayed = 1;
break;
}
case ConfigureNotify:
{
XConfigureEvent cevent = event.xconfigure;
reshapeCB(cevent.width, cevent.height);
break;
}
case KeyPress:
{
char chr;
KeySym symbol;
XComposeStatus status;
XLookupString(&event.xkey, &chr, 1, &symbol, &status);
keyboardCB(symbol, chr, event.xkey.x, event.xkey.y,
&setting_change);
break;
}
case ClientMessage:
{
if (event.xclient.message_type == wm_protocols &&
(Atom)event.xclient.data.l[0] == wm_delete_window) {
exit(EXIT_SUCCESS);
}
break;
}
}
}
}
void mainLoop() {
/* Register to receive window close events (the "X" window manager button) */
Atom wm_protocols = XInternAtom(Win.display, "WM_PROTOCOLS" , False);
Atom wm_delete_window = XInternAtom(Win.display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(Win.display, Win.win, &wm_delete_window, True);
while (1) {
/* Redraw window (after it's mapped) */
if (Win.displayed)
displayCB();
/* Update frame rate */
struct timeval last_xcheck = {0, 0};
struct timeval now;
gettimeofday(&now, 0);
/* Check X events every 1/10 second */
if (elapsedMsec(&last_xcheck, &now) > 100) {
processXEvents(wm_protocols, wm_delete_window);
last_xcheck = now;
}
}
}
int main(int argc, char *argv[]) {
Win.width = WIN_XRES;
Win.height = WIN_YRES;
createWindow();
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
printf("Valid keys: Left, Right, k, ESC\n");
printf("Press ESC to quit\n");
mainLoop();
return EXIT_SUCCESS;
}
TODO 在 Ubuntu 20.04 上失败了:
Invalid MIT-MAGIC-COOKIE-1 keyCannot open X display
Segmentation fault (core dumped)
和 Debian 8:
Failed to get GLXFBConfig
人们总是可以打开FreeGlut的源代码,看看它是如何实现每个 glut 功能的,但低于 GLX 的级别可能是硬核。
EGL
看起来像是 GLX 的 Khronos 标准化替代品,目前最常与 OpenGL ES 一起使用。
https://cgit.freedesktop.org/mesa/demos/tree/src/egl包含使用 Mesa 实现的示例,但我还没有设法使它们工作:
git checkout mesa-demos-8.1.0
./autogen.sh
./configure
make
失败:
/work/git/mesa-demos/src/egl/opengl/demo1.c:26: undefined reference to `eglGetScreensMESA'
但是Ubuntu 14.04已经es2gears
在mesa-utils-extra
包里了,所以一定有办法。
另请参阅:什么是 EGL 以及如何使用它
您可以获取GLUT 源代码并查看您关注的任何平台的初始化代码。
GL 是一个 API,而 GLU 是一个在 GL 之上的实用程序库。它完全独立于操作系统。
OpenGL 初始化和扩展获取是平台相关的操作。因此,仅使用 OpenGL 就无能为力。
GLUT 是一个快速不足且非常糟糕的库,它所做的只是它初始化了 opengl 上下文并提供了一些原始的鼠标/键盘输入模块来让你继续前进。
Win32 也提供了初始化 opengl 上下文的工具。对于 linux,您可以查看 GLX。此外,如果您想要一种独立于系统的方式,那么您可以查看 SDL。对于不同的编程语言,可能会有一些实用程序为您提供独立于平台的桌面 API。
您可以查看 Galaxy Forces V2 的来源,http://www.galaxy-forces.com/
它在不使用 GLUT 或 Windows、Mac 和 Linux 上的其他库的情况下为 OpenGL 实现类。使用本机平台代码,所有公共领域。
这是一个关于如何在不使用 GLUT 的情况下初始化 OpenGL(假设是 windows)的基本且很好的介绍:
正如 Luke 所说,如果您不想使用 GLUT,则需要有关您正在开发的操作系统的特定信息。使用 GLUT 将使您的代码更容易移植。