首页 > 代码库 > OpenGL技术之摆脱GLUT的束缚

OpenGL技术之摆脱GLUT的束缚

作者:i_dovelemon

日期:2017-03-12

来源:CSDN

主题:OpenGL, Render Context



引言



        才开始学习OpenGL的同学,大部分都是通过使用GLUT来进行学习的。确实,GLUT为我们简化了很多的工作,使得我们能够专心的学习OpenGL相关的知识,而忽略和操作系统相关的操作。但是,当我们需要自己管理操作系统的相关操作的时候,GLUT的限制就出现了,所以今天,我们就来讲讲如何在不使用GLUT的情况下,在Windows上自行管理窗口,使用OpenGL进行绘制。


渲染上下文(Render Context)



        OpenGL的所有调用命令都是在一个渲染上下文中进行的,而创建渲染上下文的API并不是由OpenGL标准来定义,而是由具体的操作系统平台来定义的。如果你想要使用OpenGL进行绘图的操作,你就必须要创建一个绘图上下文,然后通过对应操作系统的API将上下文中绘制的东西显示到屏幕上。


像素格式(Pixel Format)



        当我们用OpenGL渲染上下文进行渲染的时候,我们需要为显示的像素指定格式,然后询问操作系统,此种格式是否被支持,如果支持就使用该格式,反之则使用与之相近的格式进行设置。

        在windows平台,这个描述像素格式的结果为:PIXELFORMATDESCRIPTOR。


设置像素格式



        在Window平台上,绘制图像一般是通过GDI接口中的设备上下文(Device Context, DC)来进行的。所以,如果寻求像素,设置像素格式自然需要通过它来进行。

        当我们创建了窗口之后,我们就能够通过GetDC(HWND)函数来获取相对应的设备上下文。后面我们只要通过如下的函数调用来询问,设置像素格式即可:

    g_DC = GetDC(g_Hwnd);

    // Set surface pixel format
    PIXELFORMATDESCRIPTOR pixel_desc;
    pixel_desc.nSize = sizeof(pixel_desc); //WORD  nSize;
    pixel_desc.nVersion = 1; //WORD  nVersion;
    pixel_desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;//DWORD dwFlags;
    pixel_desc.iPixelType = PFD_TYPE_RGBA; //BYTE  iPixelType;
    pixel_desc.cColorBits = 32; //BYTE  cColorBits;
    pixel_desc.cRedBits = 0;//BYTE  cRedBits;
    pixel_desc.cRedShift = 0;//BYTE  cRedShift;
    pixel_desc.cGreenBits = 0;//BYTE  cGreenBits;
    pixel_desc.cGreenShift = 0;//BYTE  cGreenShift;
    pixel_desc.cBlueBits = 0;//BYTE  cBlueBits;
    pixel_desc.cBlueShift = 0;//BYTE  cBlueShift;
    pixel_desc.cAlphaBits = 0;//BYTE  cAlphaBits;
    pixel_desc.cAlphaShift = 0;//BYTE  cAlphaShift;
    pixel_desc.cAccumBits = 0;//BYTE  cAccumBits;
    pixel_desc.cAccumRedBits = 0;//BYTE  cAccumRedBits;
    pixel_desc.cAccumGreenBits = 0;//BYTE  cAccumGreenBits;
    pixel_desc.cAccumBlueBits = 0;//BYTE  cAccumBlueBits;
    pixel_desc.cAccumAlphaBits = 0;//BYTE  cAccumAlphaBits;
    pixel_desc.cDepthBits = 24;//BYTE  cDepthBits;
    pixel_desc.cStencilBits = 8;//BYTE  cStencilBits;
    pixel_desc.cAuxBuffers = 0;//BYTE  cAuxBuffers;
    pixel_desc.iLayerType = PFD_MAIN_PLANE;//BYTE  iLayerType;
    pixel_desc.bReserved = 0;//BYTE  bReserved;
    pixel_desc.dwLayerMask = 0;//DWORD dwLayerMask;
    pixel_desc.dwVisibleMask = 0;//DWORD dwVisibleMask;
    pixel_desc.dwDamageMask = 0;//DWORD dwDamageMask;

    int pixel_fmt = ChoosePixelFormat(g_DC, &pixel_desc);
    if (pixel_fmt == 0) {
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return;
    }

    if (SetPixelFormat(g_DC, pixel_fmt, &pixel_desc) == FALSE) {
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return;
    }

    int n = GetPixelFormat(g_DC);
	DescribePixelFormat(g_DC, n, sizeof(pixel_desc), &pixel_desc);



创建渲染上下文



        当我们为DC指定了我们想要的像素格式之后,我们就需要通过操作系统提供的函数来创建OpenGL的渲染上下文。在Windows操作系统中,和OpenGL渲染上下文相关的函数都是以wgl开头的,这里我们需要使用的是如下三个函数:wglCreateContext, wglMakeCurrent, wglDeleteContext。

        以下是如何使用这些函数的相关代码。

        首先,我们需要根据DC创建渲染上下文,并将它设置为当前使用的渲染上下文,如下调用所示:

    // Create opengl render context
    g_GLRC = wglCreateContext(g_DC);
    if (g_GLRC == NULL) {
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return;
    }
    wglMakeCurrent(g_DC, g_GLRC);

        当我们使用完毕渲染上下文的时候,我们就可以删除它,如下代码:

    wglMakeCurrent(g_DC, NULL);
    wglDeleteContext(g_GLRC);



渲染



        当我们创建好渲染上下文,并且将它设置为当前启用的渲染上下文之后,我们就可以通过OpenGL的API调用来渲染我们需要的场景。当我们将OpenGL的渲染命令Flush了之后,我们就可以通过操作系统提供的SwapBuffers函数来将绘制的图像显示在屏幕上。


相关设置



        为了能够使用wglCreateContext, wglDeleteContext, wglMakeCurrent这几个函数,我们需要链接Opengl32.lib到我们的项目中去,同时为了使用OpenGL的相关扩展,我们可以手动获取相关的函数,也可以通过GLEW库来实现。我这里通过GLEW来实现。


完整代码



        以下是完整的使用该方法进行OpenGL渲染的代码:

#include <Windows.h>

#include <GL\glew.h>

//--------------------------------------------------------------------------------------
// Variables
//--------------------------------------------------------------------------------------
HWND g_Hwnd;
HDC g_DC;
HGLRC g_GLRC;

//--------------------------------------------------------------------------------------
// Function Declaration
//--------------------------------------------------------------------------------------
void opengl_init();
void opengl_run();
void opengl_destroy();

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

//--------------------------------------------------------------------------------------
// Function Definition
//--------------------------------------------------------------------------------------
int _stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR cmdLine, int nShowCmd) {
    // Register window class
    WNDCLASSEX wnd;
    wnd.cbClsExtra = 0;
    wnd.cbSize = sizeof(wnd);
    wnd.cbWndExtra = NULL;
    wnd.hbrBackground = HBRUSH(COLOR_WINDOW + 1);
    wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
    wnd.hIcon = NULL;
    wnd.hIconSm = NULL;
    wnd.hInstance = hInstance;
    wnd.lpfnWndProc = WndProc;
    wnd.lpszClassName = L"OpenGLRC";
    wnd.lpszMenuName = NULL;
    wnd.style = CS_HREDRAW | CS_VREDRAW;

    if (!RegisterClassEx(&wnd)) {
        MessageBox(NULL, NULL, L"Fuck you", MB_OK);
        return -1;
    }

    // Create window
    RECT client_rect = {0, 0, 800, 600};
    AdjustWindowRect(&client_rect, WS_OVERLAPPEDWINDOW, FALSE);
    g_Hwnd = CreateWindowEx(0, L"OpenGLRC", L"OpenGL", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT
        , client_rect.right - client_rect.left, client_rect.bottom - client_rect.top, NULL, NULL, hInstance, NULL);
    if (g_Hwnd == NULL) {
        int err = GetLastError();
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return -1;
    }

    ShowWindow(g_Hwnd, nShowCmd);

    opengl_init();

    // Message loop
    MSG msg = {0};
    while (msg.message != WM_QUIT) {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        } else {
            opengl_run();
        }
    }

    opengl_destroy();

    return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

void opengl_init() {
    g_DC = GetDC(g_Hwnd);

    // Set surface pixel format
    PIXELFORMATDESCRIPTOR pixel_desc;
    pixel_desc.nSize = sizeof(pixel_desc); //WORD  nSize;
    pixel_desc.nVersion = 1; //WORD  nVersion;
    pixel_desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;//DWORD dwFlags;
    pixel_desc.iPixelType = PFD_TYPE_RGBA; //BYTE  iPixelType;
    pixel_desc.cColorBits = 32; //BYTE  cColorBits;
    pixel_desc.cRedBits = 0;//BYTE  cRedBits;
    pixel_desc.cRedShift = 0;//BYTE  cRedShift;
    pixel_desc.cGreenBits = 0;//BYTE  cGreenBits;
    pixel_desc.cGreenShift = 0;//BYTE  cGreenShift;
    pixel_desc.cBlueBits = 0;//BYTE  cBlueBits;
    pixel_desc.cBlueShift = 0;//BYTE  cBlueShift;
    pixel_desc.cAlphaBits = 0;//BYTE  cAlphaBits;
    pixel_desc.cAlphaShift = 0;//BYTE  cAlphaShift;
    pixel_desc.cAccumBits = 0;//BYTE  cAccumBits;
    pixel_desc.cAccumRedBits = 0;//BYTE  cAccumRedBits;
    pixel_desc.cAccumGreenBits = 0;//BYTE  cAccumGreenBits;
    pixel_desc.cAccumBlueBits = 0;//BYTE  cAccumBlueBits;
    pixel_desc.cAccumAlphaBits = 0;//BYTE  cAccumAlphaBits;
    pixel_desc.cDepthBits = 24;//BYTE  cDepthBits;
    pixel_desc.cStencilBits = 8;//BYTE  cStencilBits;
    pixel_desc.cAuxBuffers = 0;//BYTE  cAuxBuffers;
    pixel_desc.iLayerType = PFD_MAIN_PLANE;//BYTE  iLayerType;
    pixel_desc.bReserved = 0;//BYTE  bReserved;
    pixel_desc.dwLayerMask = 0;//DWORD dwLayerMask;
    pixel_desc.dwVisibleMask = 0;//DWORD dwVisibleMask;
    pixel_desc.dwDamageMask = 0;//DWORD dwDamageMask;

    int pixel_fmt = ChoosePixelFormat(g_DC, &pixel_desc);
    if (pixel_fmt == 0) {
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return;
    }

    if (SetPixelFormat(g_DC, pixel_fmt, &pixel_desc) == FALSE) {
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return;
    }

    int n = GetPixelFormat(g_DC);
	DescribePixelFormat(g_DC, n, sizeof(pixel_desc), &pixel_desc);

    // Create opengl render context
    g_GLRC = wglCreateContext(g_DC);
    if (g_GLRC == NULL) {
        MessageBox(NULL, NULL, L"Error", MB_OK);
        return;
    }
    wglMakeCurrent(g_DC, g_GLRC);

    // Init glew
    glewInit();
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
}

void opengl_run() {
    GLfloat clear_c[4] = {rand() % 100 / 100.0f, rand() % 100 / 100.0f
    ,rand() % 100 / 100.0f, 1.0f};
    glClearColor(clear_c[0], clear_c[1], clear_c[2], 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();
    SwapBuffers(g_DC);
}

void opengl_destroy() {
    wglMakeCurrent(g_DC, NULL);
    wglDeleteContext(g_GLRC);
    ReleaseDC(g_Hwnd, g_DC);
}

总结



        有了这个方法,我们就能够自行的管理窗口相关的操作,接下来我将根据这个方法来修改GLB代码库,以便于后面对DX11进行支持。

OpenGL技术之摆脱GLUT的束缚