- Windows 只提供了 GL v1.1 ;如何下载最新版本的 OpenGL 二进制文件 (+hearders) 以开始在 Windows 中编码,以及在哪里可以找到它们?
- 哪个版本的 OpenGL 是所有三个 MacOS、Linux 和 Windows 平台完全支持的最新版本?
4 回答
OpenGL 是一个开放的标准。最新版本取决于您为 GPU 安装的驱动程序。
要使用高于 1.1 的 opengl,您需要 glew(它公开了您的驱动程序支持的功能)和一个用于创建上下文的框架:
GLFW、过剩、SDL
和更多。
当前最新的 OpenGL 实现:4.5
没有适用于 Windows 的更新 SDK,仅gl.h
提供适用于 OpenGL 1.1 的 SDK。要在 Window OpenGL 中使用附加功能,您必须使用扩展头glext.h。这可从 Khronos 获得,并在新的扩展可用时更新。您可以使用wglGetProcAddress
获取函数指针,以指向更高版本的 GL(或扩展)中可用的函数。访问附加功能的过程因平台而异。例如,如果您使用 X11,则可以使用glXGetProcAddressARB
.,如果您的平台上有 EGL,则可以使用eglGetProcAddress
.
根据平台和 SDK,如果平台提供了更新的 GL SDK ,您也可以#define GL_GLEXT_PROTOTYPES
在包含之前简单地添加。glext.h
显然,由于 Windows 没有提供更新的 SDK,所以这在那里是不可能的,但它在 Linux、Mac、iOS 和 Android 上是可行的。在这两种方法中,您都需要确保您使用的 GL 上下文支持核心 API 中的功能,或者提供该功能的扩展可用(请参阅)) glGetString
,GL_EXTENSIONS
您构建的 SDK 是不够的包含功能。因此,使用“运行时链接”glGetProcAddress
方法更安全一些,因为如果该函数不可用,它只会返回NULL
. 使用不可用的函数将导致未定义的行为,并且可能会崩溃。
至于任何给定平台上支持的最新版本,这取决于您的视频驱动程序,而不是您构建的 SDK。当您创建 OpenGL 上下文时,glGetString
w/GL_VERSION
将指示您的上下文支持的 OpenGL 版本,无论您使用哪个版本的 GL SDK 构建。您可以使用版本保证的任何功能,以及报告的扩展提供的任何功能。
一般来说,OpenGL wiki有所有链接,可以为您的平台(操作系统和显卡)下载最新的 OpenGL。特别是对于 Windows,如果您使用的是 Visual Studio,NuGet 包管理器有一个很好的预打包 OpenGL 发行版,称为“nupengl.core”。
正如Arctic所说,4.5是最新标准。您的构建是否支持它取决于显卡,而不是操作系统。
OpenGL 只是一个标准。我认为 OpenGL Khronos 小组只提供了 OpenGL 实现的标头,没有别的。显卡供应商为其显卡提供了一个驱动程序,该驱动程序链接到操作系统内核。这里的问题是您不知道驱动程序是如何工作的。因此,供应商必须为用户模式库提供他们的驱动程序,以便软件实现者能够驱动卡。这通常以用户模式库形式的 OpenGL/Direct3D 实现的形式出现,可以从用户模式程序链接。
另外,我不是专家,但我可以告诉您,您需要创建一个 OpenGL 上下文并将此上下文与平台相关上下文链接,该上下文是渲染内容的实际窗口。这意味着当您的线程执行 OpenGL 命令时,您需要告诉操作系统在哪里绘制。否则,您的 OpenGL 命令/代码行不会做任何事情,因为它们没有与窗口/设备上下文链接。
例如,在 Linux 上,您有一个名为 /dev/dri/card0 的文件,它是基于 Intel/AMD 的计算机上的集成显卡。OpenGL 库实现将以以下形式进行系统调用ioctl()
调用此字符设备来驱动卡。下面是硬件供应商提供的驱动程序,它在内核中注册自己,并通过一些功能告诉内核该驱动程序是图形驱动程序,并且它支持某些类型的设备,其中包括 /dev/dri/card0字符设备。它可能还传递了一个 PCI ID,这是显卡在读取其 PCI 配置空间时返回的设备 ID。因此,操作系统内核能够识别该驱动程序必须用于某个字符设备,因为该设备的 ID 与驱动程序在注册时提供的 ID 相同。
提供了一些库,例如 glut、freeglut 和 GLFW,恕我直言,它们非常糟糕。它们通常不起作用并且很难使用,因为它们通常最终会产生很多链接问题,并且名副其实的游戏/3D 开发人员不会使用它们。这些库仅提供窗口和用户输入功能。因此,这些库毫无用处,因为在 Linux 上使用 X 服务器创建窗口和获取用户输入需要 5 行代码,而在 Windows 上相当于 1 行代码和所谓的窗口过程。自己实现真的很简单。实际上,这些库(freeglut、GLFW 等)使用 OS 内核开发人员提供的平台相关 API。他们只把这些包装在一些#ifdef
(宏)提供跨平台能力。最后,重现这些库所做的工作是微不足道的,而且通常比实际使用这些库更好。
此外,Windows 仅提供开箱即用的 OpenGL 1.1 支持。因此,您可以在 Visual Studio IDE 中随时使用和定义这些函数。您可以看到这些函数以棕色突出显示,当您将鼠标悬停在顶部时,您可以看到函数的签名。GLEW(不是 GLFW)库被发现对于初学者(像我一样)能够在 1.1 以上的 OpenGL 中开始编码而不需要更多工作非常有用。GLEW 库将测试 OpenGL 函数的存在,并在 Windows 上使用 GetProcAdress() 或 wglGetProcAddress() 获取过程地址,它是跨平台的(更多信息:https ://www.khronos.org/opengl/wiki/ Load_OpenGL_Functions)。该库需要一个名为 glew32.lib 的静态 .lib 文件、一个名为 glew32.dll 的 .dll(放在可执行文件的目录中)和一个名为 glew.h 的程序头文件。现在我不会告诉您如何将包含和库目录添加到 Visual Studio,但您可以在 Google 上的某个地方查看。GLEW 提供的 OpenGL 函数将以宏的形式出现。您将在 Visual Studio 中以紫色看到它们。
例如,使用 WinAPI 和 Visual Studio 在 Windows 上执行与 GLFW 相同的简单 OpenGL 程序如下所示:
#include <windows.h>
#include <glew.h>
#include <gl/GL.h>
#include <iostream>
#pragma comment(lib, "glew32")
#pragma comment(lib, "opengl32")
#pragma comment(lib, "glu32")
HDC dc;
HGLRC rc;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg)
{
case WM_PAINT:
{
}
break;
case WM_LBUTTONDOWN:
{
}
break;
case WM_CLOSE:
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
}
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Window";
RegisterClass(&wc);
HWND hwnd = CreateWindowExW(0, L"Window", L"Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
AllocConsole();
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
MSG msg = { };
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
dc = GetDC(hwnd);
int index;
index = ChoosePixelFormat(dc, &pfd);
SetPixelFormat(dc, index, &pfd);
rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
GLenum res = glewInit();
if (res != GLEW_OK) {
printf("GLEW init failed: %s!", glewGetErrorString(res));
}
else {
printf("GLEW init success!\n");
}
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(dc);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
这个程序应该创建一个空白窗口,创建一个控制台,打印“GLEW init sucess!” 在控制台上,然后将窗口的背景设置为黑色。您会看到这段代码非常简单,这就是 GLFW 在其“跨平台”API 下所做的事情。就像我说的,我是一个初学者,但我认为,在这种情况下,OpenGL 调用将写入双缓冲区,因为我们在像素格式结构中启用了双缓冲区。要在屏幕上实际渲染某些内容,需要使用窗口的 dc 调用 SwapBuffers() 以将双缓冲区与实际绘制的主缓冲区交换。