当前位置: 首页 > 编程笔记 >

opengl 在Windows上手动设置OpenGL

穆正祥
2023-03-14
本文向大家介绍opengl 在Windows上手动设置OpenGL,包括了opengl 在Windows上手动设置OpenGL的使用技巧和注意事项,需要的朋友参考一下

示例

最后包含完整的示例代码

OpenGL的Windows组件

WGL

WGL(可以发音为wiggle)代表“ Windows-GL”,如“ Windows和OpenGL之间的接口”一样-Windows API中与OpenGL通信的一组函数。WGL函数具有wgl前缀,其令牌具有WGL_前缀。

Microsoft系统上支持的默认OpenGL版本是1.1。那是一个非常旧的版本(最新版本是4.5)。获得最新版本的方法是更新图形驱动程序,但是图形卡必须支持这些新版本。

WGL功能的完整列表可在此处找到。

图形设备接口(GDI)

GDI(今天已更新为GDI +)是2D绘图界面,可用于在Windows中绘制到窗口上。您需要GDI来初始化OpenGL并允许它与OpenGL交互(但实际上不会使用GDI本身)。

在GDI中,每个窗口都有一个设备上下文(DC),用于在调用函数时标识绘制目标(将其作为参数传递)。但是,OpenGL使用其自己的渲染上下文(RC)。因此,DC将用于创建RC。

基本设定

创建一个窗口

因此,要在OpenGL中执行操作,我们需要RC,要获得RC,我们需要DC,要获得DC,我们需要一个窗口。使用Windows API创建窗口需要几个步骤。这是一个基本例程,因此,对于更详细的解释,您应该查阅其他文档,因为这与使用Windows API有关。

这是Windows安装程序,因此Windows.h必须包括在内,并且程序的入口点必须是WinMain带有其参数的过程。该程序还需要链接到opengl32.dll和链接gdi32.dll(无论您使用的是64位还是32位系统)。

首先,我们需要使用该WNDCLASS结构描述窗口。它包含有关我们要创建的窗口的信息:

/* REGISTER WINDOW */
WNDCLASS window_class;

// 首先清除所有结构字段为零
ZeroMemory(&window_class, sizeof(window_class));

// 定义我们需要的字段(其他字段为零)
window_class.style = CS_OWNDC;
window_class.lpfnWndProc = window_procedure; // 稍后介绍
window_class.hInstance = instance_handle;
window_class.lpszClassName = TEXT("OPENGL_WINDOW");

// 给我们的班上Windows
RegisterClass(&window_class);
/* *************** */

有关每个字段含义的精确解释(以及完整的字段列表),请查阅MSDN文档。

然后,我们可以使用创建一个窗口CreateWindowEx。创建窗口后,我们可以获取其DC:

/* CREATE WINDOW */
HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                                    TEXT("OPENGL_WINDOW"),
                                    TEXT("OpenGL window"),
                                    WS_OVERLAPPEDWINDOW,
                                    0, 0,
                                    800, 600,
                                    NULL,
                                    NULL,
                                    instance_handle,
                                    NULL);

HDC dc = GetDC(window_handle);

ShowWindow(window_handle, SW_SHOW);
/* ************* */

最后,我们需要创建一个消息循环,以从OS接收窗口事件:

/* EVENT PUMP */
MSG msg;

while (true) {
    if (PeekMessage(&msg, window_handle, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT)
            break;
        
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    // draw(); <- there goes your drawing

    SwapBuffers(dc); // 待稍后提及
}
/* ********** */

像素格式

OpenGL需要了解有关我们窗口的一些信息,例如颜色位数,缓冲方法等。为此,我们使用像素格式。但是,我们只能向OS建议我们需要哪种像素格式,而OS将提供最相似的受支持的像素格式,我们无法对其进行直接控制。这就是为什么它仅称为描述符的原因

/* PIXEL FORMAT */
PIXELFORMATDESCRIPTOR descriptor;

// 首先清除所有结构字段为零
ZeroMemory(&descriptor, sizeof(descriptor));

// 描述我们的像素格式
descriptor.nSize = sizeof(descriptor);
descriptor.nVersion = 1;
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
descriptor.iPixelType = PFD_TYPE_RGBA;
descriptor.cColorBits = 32;
descriptor.cRedBits = 8;
descriptor.cGreenBits = 8;
descriptor.cBlueBits = 8;
descriptor.cAlphaBits = 8;
descriptor.cDepthBits = 32;
descriptor.cStencilBits = 8;

// 要求类似的支持格式并进行设置
int pixel_format = ChoosePixelFormat(dc, &descriptor);
SetPixelFormat(dc, pixel_format, &descriptor);
/* *********************** */

我们已经在该dwFlags字段中启用了双重缓冲,因此我们必须调用SwapBuffers才能在绘制后看到事物。

渲染上下文

之后,我们可以简单地创建渲染上下文:

/* RENDERING CONTEXT */
HGLRC rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
/* ***************** */

请注意,一次只有一个线程可以使用RC。如果您希望以后在另一个线程中使用它,则必须wglMakeCurrent在此处调用以再次激活它(这将在当前处于活动状态的线程上将其禁用,依此类推)。

获取OpenGL函数

OpenGL函数是通过使用函数指针获得的。通用程序为:

  1. 以某种方式获取函数指针类型(本质上是函数原型)

  2. 声明我们要使用的每个函数(及其函数指针类型)

  3. 获得实际功能

例如,考虑glBegin:

// 我们需要以某种方式找到包含此类内容的内容,
// 因为我们不知道所有的OpenGL函数原型
typedef void (APIENTRY *PFNGLBEGINPROC)(GLenum);

// 之后,我们需要声明该函数才能使用它
PFNGLBEGINPROC glBegin;

// 最后,我们需要以某种方式使其成为实际功能

(“ PFN”的意思是“函数的指针”,然后跟随OpenGL函数的名称,最后是“ PROC”,这是通常的OpenGL函数指针类型的名称。)

这是在Windows上完成的方式。如前所述,Microsoft仅提供OpenGL 1.1。首先,可以通过包含来找到该版本的函数指针类型GL/gl.h。之后,我们声明要使用的所有函数,如上所示(在头文件中进行声明并声明为“ extern”将使我们在加载它们一次之后就可以全部使用它们,只需将其包括在内)。最后,通过打开DLL来加载OpenGL 1.1函数:

HMODULE gl_module = LoadLibrary(TEXT("opengl32.dll"));

/* Load all the functions here */
glBegin = (PFNGLBEGINPROC)GetProcAddress("glBegin");
// ...
/* *************************** */

FreeLibrary(gl_module);

但是,我们可能需要的不仅仅是OpenGL 1.1。但是Windows并没有为我们提供任何以上功能的函数原型或导出的函数。需要从OpenGL注册中心获取原型。有三个文件我们感兴趣的:GL/glext.h,GL/glcorearb.h,和GL/wglext.h。

为了完成GL/gl.hWindows提供的服务,我们需要GL/glext.h。它包含(如注册表所述)“ OpenGL 1.2及更高版本的兼容性配置文件和扩展接口”(稍后将详细介绍配置文件和扩展,在此我们会发现使用这两个文件实际上不是一个好主意)。

需要通过获取实际功能wglGetProcAddress(无需为此家伙打开DLL,它们不在其中,只需使用该功能即可)。有了它,我们可以从OpenGL 1.2及更高版本(而不是1.1)中获取所有功能。请注意,为了使其正常运行,必须创建OpenGL渲染上下文并将其设置为current。因此,例如glClear:

// 包括来自OpenGL注册表的标头,用于函数指针类型

// 像以前一样声明功能
PFNGLCLEARPROC glClear;
// ...

// 获取功能
glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");

实际上,我们可以构建一个get_proc同时使用wglGetProcAddress和的包装程序GetProcAddress:

// 获取函数指针
void* get_proc(const char *proc_name)
{
    void *proc = (void*)wglGetProcAddress(proc_name);
    if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name); // gl_module必须在可以到达的地方

    return proc;
}

最后,我们将创建一个充满函数指针声明的头文件,如下所示:

extern PFNGLCLEARCOLORPROC glClearColor;
extern PFNGLCLEARDEPTHPROC glClearDepth;
extern PFNGLCLEARPROC glClear;
extern PFNGLCLEARBUFFERIVPROC glClearBufferiv;
extern PFNGLCLEARBUFFERFVPROC glClearBufferfv;
// 等等...

然后,我们可以创建一个load_gl_functions仅调用一次的过程,其工作方式如下:

glClearColor = (PFNGLCLEARCOLORPROC)get_proc("glClearColor");
glClearDepth = (PFNGLCLEARDEPTHPROC)get_proc("glClearDepth");
glClear = (PFNGLCLEARPROC)get_proc("glClear");
glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)get_proc("glClearBufferiv");
glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)get_proc("glClearBufferfv");

一切就绪!只需将标头与函数指针和GL分开即可。

更好的设置

OpenGL配置文件

OpenGL的开发已有20多年了,开发人员始终对向后兼容性(BC)严格。因此,添加新功能非常困难。因此,在2008年,它被分为两个“配置文件”。核心兼容性。核心配置文件打破了BC,而支持性能改进和一些新功能。它甚至完全删除了一些旧功能。兼容性配置文件将BC的所有版本降至1.0以下,并且某些新功能不可用。它仅用于旧的旧系统,所有新应用程序都应使用核心配置文件。

因此,我们的基本设置存在问题-它仅提供了与OpenGL 1.0向后兼容的上下文。像素格式也受到限制。有一个更好的方法,使用扩展。

OpenGL扩展

OpenGL原始功能的任何附加功能都称为扩展。通常,他们可以使某些以前没有的事情合法化,扩展参数值范围,扩展GLSL甚至添加全新的功能。

扩展分为三大类:供应商,EXT和ARB。供应商扩展来自特定的供应商,并且具有特定于供应商的标记,例如AMD或NV。EXT扩展是由多家供应商共同完成的。一段时间后,它们可能会成为ARB扩展名,它们都是官方支持的和ARB批准的扩展名。

要获取所有扩展的函数指针类型和函数原型,并且如上所述,OpenGL 1.2及更高版本中的所有函数指针类型都必须从OpenGL注册表中下载头文件。如前所述,对于新应用程序,最好使用核心配置文件,因此最好使用includeGL/glcorearb.h而不是GL/gl.hand GL/glext.h(如果正在使用,GL/glcorearb.h则不要包含GL/gl.h)。

在中也有WGL的扩展GL/wglext.h。例如,用于获取所有受支持扩展的列表的功能实际上是扩展本身,wglGetExtensionsStringARB(它返回一个大字符串,并以空格分隔所有受支持扩展的列表)。

获取扩展也通过来处理wglGetProcAddress,因此我们可以像以前一样使用包装器。

高级像素格式和上下文创建

该WGL_ARB_pixel_format扩展允许我们创建高级像素格式。与以前不同,我们不使用结构。相反,我们传递所需属性的列表。

int pixel_format_arb;
UINT pixel_formats_found;

int pixel_attributes[] = {
    WGL_SUPPORT_OPENGL_ARB, 1,
    WGL_DRAW_TO_WINDOW_ARB, 1,
    WGL_DRAW_TO_BITMAP_ARB, 1,
    WGL_DOUBLE_BUFFER_ARB, 1,
    WGL_SWAP_LAYER_BUFFERS_ARB, 1,
    WGL_COLOR_BITS_ARB, 32,
    WGL_RED_BITS_ARB, 8,
    WGL_GREEN_BITS_ARB, 8,
    WGL_BLUE_BITS_ARB, 8,
    WGL_ALPHA_BITS_ARB, 8,
    WGL_DEPTH_BITS_ARB, 32,
    WGL_STENCIL_BITS_ARB, 8,
    WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
    0
};

BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);

同样,该WGL_ARB_create_context扩展允许我们进行高级上下文创建:

GLint context_attributes[] = {
    WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
    WGL_CONTEXT_MINOR_VERSION_ARB, 3,
    WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
    0
};

HGLRC new_rc = wglCreateContextAttribsARB(dc, 0, context_attributes);

有关参数和功能的精确说明,请参阅OpenGL规范。

我们为什么不从他们开始呢?好吧,那是因为扩展允许我们执行此操作,并获得我们需要的扩展wglGetProcAddress,但这仅在活动的有效上下文中有效。因此,从本质上讲,在我们能够创建所需上下文之前,我们需要先激活一些上下文,通常将其称为虚拟上下文

但是,Windows不允许多次设置窗口的像素格式。因此,为了应用新事物,需要销毁并重新创建窗口:

wglMakeCurrent(dc, NULL);
wglDeleteContext(rc);
ReleaseDC(window_handle, dc);
DestroyWindow(window_handle);

// 重新创建窗口...

完整的示例代码:

/* We want the core profile, so we include GL/glcorearb.h. When including that, then
   GL/gl.h should not be included.

   If using compatibility profile, the GL/gl.h and GL/glext.h need to be included.

   GL/wglext.h gives WGL extensions.

   Note thatWindows.hneeds to be included before them. */

#include <cstdio>
#include <Windows.h>
#include <GL/glcorearb.h>
#include <GL/wglext.h>

LRESULT CALLBACK window_procedure(HWND, UINT, WPARAM, LPARAM);
void* get_proc(const char*);

/* gl_module is for opening the DLL, and the quit flag is here to prevent
   quitting when recreating the window (see the window_procedure function) */

HMODULE gl_module;
bool quit = false;

/* OpenGL function declarations. In practice, we would put these in a
   separate header file and add "extern" in front, so that we can use them
   anywhere after loading them only once. */

PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
PFNGLGETSTRINGPROC glGetString;

int WINAPI WinMain(HINSTANCE instance_handle, HINSTANCE prev_instance_handle, PSTR cmd_line, int cmd_show) {
    /* REGISTER WINDOW */
    WNDCLASS window_class;

    // 首先清除所有结构字段为零
    ZeroMemory(&window_class, sizeof(window_class));

    // 定义我们需要的字段(其他字段为零)
    window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    window_class.lpfnWndProc = window_procedure;
    window_class.hInstance = instance_handle;
    window_class.lpszClassName = TEXT("OPENGL_WINDOW");

    // 给我们的班上Windows
    RegisterClass(&window_class);
    /* *************** */
        
    /* CREATE WINDOW */
    HWND window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                                        TEXT("OPENGL_WINDOW"),
                                        TEXT("OpenGL window"),
                                        WS_OVERLAPPEDWINDOW,
                                        0, 0,
                                        800, 600,
                                        NULL,
                                        NULL,
                                        instance_handle,
                                        NULL);
        
    HDC dc = GetDC(window_handle);
        
    ShowWindow(window_handle, SW_SHOW);
    /* ************* */
        
    /* PIXEL FORMAT */
    PIXELFORMATDESCRIPTOR descriptor;
        
    // 首先清除所有结构字段为零
    ZeroMemory(&descriptor, sizeof(descriptor));
        
    // 描述我们的像素格式
   descriptor.nSize= sizeof(descriptor);
   descriptor.nVersion= 1;
   descriptor.dwFlags= PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED | PFD_DOUBLEBUFFER | PFD_SWAP_LAYER_BUFFERS;
   descriptor.iPixelType= PFD_TYPE_RGBA;
   descriptor.cColorBits= 32;
   descriptor.cRedBits= 8;
   descriptor.cGreenBits= 8;
   descriptor.cBlueBits= 8;
   descriptor.cAlphaBits= 8;
   descriptor.cDepthBits= 32;
   descriptor.cStencilBits= 8;
        
    // 要求类似的支持格式并进行设置
    int pixel_format = ChoosePixelFormat(dc, &descriptor);
    SetPixelFormat(dc, pixel_format, &descriptor);
    /* *********************** */
        
    /* RENDERING CONTEXT */
    HGLRC rc = wglCreateContext(dc);
    wglMakeCurrent(dc, rc);
    /* ***************** */

    /* LOAD FUNCTIONS (should probably be put in a separate procedure) */
    gl_module = LoadLibrary(TEXT("opengl32.dll"));

    wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)get_proc("wglGetExtensionsStringARB");
    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)get_proc("wglChoosePixelFormatARB");
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)get_proc("wglCreateContextAttribsARB");
    glGetString = (PFNGLGETSTRINGPROC)get_proc("glGetString");
    
    FreeLibrary(gl_module);
    /* ************** */

    /* PRINT VERSION */
    const GLubyte *version = glGetString(GL_VERSION);
    printf("%s\n", version);
    fflush(stdout);
    /* ******* */

    /* NEW PIXEL FORMAT*/
    int pixel_format_arb;
    UINT pixel_formats_found;
    
    int pixel_attributes[] = {
        WGL_SUPPORT_OPENGL_ARB, 1,
        WGL_DRAW_TO_WINDOW_ARB, 1,
        WGL_DRAW_TO_BITMAP_ARB, 1,
        WGL_DOUBLE_BUFFER_ARB, 1,
        WGL_SWAP_LAYER_BUFFERS_ARB, 1,
        WGL_COLOR_BITS_ARB, 32,
        WGL_RED_BITS_ARB, 8,
        WGL_GREEN_BITS_ARB, 8,
        WGL_BLUE_BITS_ARB, 8,
        WGL_ALPHA_BITS_ARB, 8,
        WGL_DEPTH_BITS_ARB, 32,
        WGL_STENCIL_BITS_ARB, 8,
        WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
        WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
        0
    };

    BOOL result = wglChoosePixelFormatARB(dc, pixel_attributes, NULL, 1, &pixel_format_arb, &pixel_formats_found);

    if (!result) {
        printf("Could not find pixel format\n");
        fflush(stdout);
        return 0;
    }
    /* **************** */

    /* RECREATE WINDOW */
    wglMakeCurrent(dc, NULL);
    wglDeleteContext(rc);
    ReleaseDC(window_handle, dc);
    DestroyWindow(window_handle);
    
    window_handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                                        TEXT("OPENGL_WINDOW"),
                                        TEXT("OpenGL window"),
                                        WS_OVERLAPPEDWINDOW,
                                        0, 0,
                                        800, 600,
                                        NULL,
                                        NULL,
                                        instance_handle,
                                        NULL);
        
    dc = GetDC(window_handle);
        
    ShowWindow(window_handle, SW_SHOW);
    /* *************** */

    /* NEW CONTEXT */
    GLint context_attributes[] = {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 3,
        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
        0
    };

    rc = wglCreateContextAttribsARB(dc, 0, context_attributes);
    wglMakeCurrent(dc, rc);
    /* *********** */
        
    /* EVENT PUMP */
    MSG msg;
        
    while (true) {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT) 
                break;
                
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
            
        // draw(); <- there goes your drawing
            
        SwapBuffers(dc);
    }
    /* ********** */
        
    return 0;
}

// 处理窗口事件的过程
LRESULT CALLBACK window_procedure(HWND window_handle, UINT message, WPARAM param_w, LPARAM param_l)
{
    /* When destroying the dummy window, WM_DESTROY message is going to be sent,
       but we don't want to quit the application then, and that is controlled by
       the quit flag. */

    switch(message) {
    case WM_DESTROY:
        if (!quit) quit = true;
        else PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(window_handle, message, param_w, param_l);
}

/* A procedure for getting OpenGL functions and OpenGL or WGL extensions.
   When looking for OpenGL 1.2 and above, or extensions, it uses wglGetProcAddress,
   otherwise it falls back to GetProcAddress. */
void* get_proc(const char *proc_name)
{
    void *proc = (void*)wglGetProcAddress(proc_name);
    if (!proc) proc = (void*)GetProcAddress(gl_module, proc_name);

    return proc;
}

编译使用MinGW / Cygwin的或与MSVC编译器。但是,请确保OpenGL注册表中的标头位于包含路径中。如果不是,请使用标志for或for告诉编译器它们在哪里。g++GLExample.cpp-lopengl32 -lgdi32clGLExample.cppopengl32.libgdi32.libuser32.lib-Ig++/Icl

 类似资料:
  • 问题内容: 我见过许多与JDK捆绑在一起的产品,我想知道是否有一种方法可以通过简单地将内容解压缩到目录中来安装JDK,因此在添加/删除程序中没有创建图标,也没有注册表项等。 同样在这种情况下:我们如何为浏览器配置Java插件?以及如何配置通过Java的“控制面板”条目看到的设置? 问题答案: 是的,您可以创建一个压缩的JDK,将其解压缩到目标计算机上,然后从该目录运行您喜欢的内容的java,jav

  • 问题内容: 在Ubuntu中,这非常简单;我可以使用以下方式运行该应用程序: 但是,这在Windows上不起作用。是否可以在其中配置属性的配置文件? 问题答案: 当前版本的Windows使用Powershell作为默认外壳,因此请使用: 根据下面的@jsalonen的答案。如果您使用的是CMD(已不再维护),请使用 这应该在您打算运行Node.js应用程序的命令提示符下执行。 上一行将在执行命令的

  • 任何想法/暗示都非常受欢迎;谢了!

  • 我试图设置一个火花3光泽使用两个系统运行Windows10。我可以开始用master ,它在启动主程序

  • 问题内容: 我转到控制面板中“系统”中的“环境变量”,并创建了两个新变量,一个用于用户变量,另一个用于系统变量。两者都被命名为JAVA_HOME并且都指向 但是由于某些原因,运行Java命令时仍然出现以下错误… 我该如何解决这个问题? 问题答案: 查找JDK安装目录 首先,你需要了解Java Development Kit的安装路径。 打开JDK的默认安装路径: 应该有一个类似的子目录: 注意:只

  • 我最近在Windows上运行的Ubuntu bash上安装并设置了rails作为Linux子系统。我能够顺利地启动rails,并在我的Windows C:/驱动器上创建一个新的rails应用程序。我尝试直接从postgresql.org/downloads/windows网站下载PSQL,但是遇到了一些问题,所以我取消了,然后按照本教程的安装PostgresQL部分进行操作。唯一的问题是,先前被取