2
\$\begingroup\$

How can I initialize OpenGl in C++ without any 3rd party library such as glew, freeglut or glut. How would I initialize basic OpenGL requirements, like the context?

I am running Windows 10, and I use Visual Studio.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ What's your operating system? It's done differently in different OSes. \$\endgroup\$ – SurvivalMachine Sep 9 '16 at 7:47
  • \$\begingroup\$ @SurvivalMachine windows 10 and can you give me example of how it is done differently on other os \$\endgroup\$ – Finnick Odair Sep 9 '16 at 9:20
  • \$\begingroup\$ Why though? You'll save yourself a lot of pain if you just use a OpenGL function loader. SDL allows you to create an OpenGL context, but it doesn't help with the function access. \$\endgroup\$ – Tyyppi_77 Sep 9 '16 at 9:25
  • 2
    \$\begingroup\$ -1 "This question does not show any research effort" \$\endgroup\$ – Vaillancourt Sep 9 '16 at 12:12
  • 3
    \$\begingroup\$ If you have questions about your questions, ask in Game Development Meta. \$\endgroup\$ – Vaillancourt Sep 12 '16 at 18:07
6
\$\begingroup\$

On Windows, the process goes like this:

  • Create a Win32 window normally (code not included, ask if you need it)
  • Create a dummy GL context
  • Query the available pixel formats
  • Create the actual GL context (in the below code inside CreateRenderer(), context version is 4.1)
  • Query OpenGL function pointers. Including code for them all would be too big so here's one.

Query the function pointer:

HMODULE libgl = LoadLibraryA("opengl32.dll");
PFNGLCULLFACEPROC glCullFace = GetProcAddress((HMODULE)libgl, "glCullFace");

Here's the rest of the code (error handling omitted for brevity):

namespace Dummy
{
    HWND hwnd;
    HDC hdc;
    HGLRC hrc;
    WNDCLASSEX wcl;
}

LRESULT CALLBACK DummyGLWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch (msg)
    {
    case WM_CREATE:
    {
        Dummy::hdc = GetDC( hWnd );

        if (!Dummy::hdc)
        {
            return -1;
        }
    }
        break;

    case WM_DESTROY:
        if (Dummy::hdc)
        {
            if (Dummy::hrc)
            {
                wglMakeCurrent( Dummy::hdc, 0 );
                wglDeleteContext( Dummy::hrc );
                Dummy::hrc = 0;
            }

            ReleaseDC( hWnd, Dummy::hdc );
            Dummy::hdc = 0;
        }

        PostQuitMessage( 0 );
        return 0;

    default:
        break;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

void CreateDummyGLWindow()
{
    Dummy::wcl.cbSize = sizeof( Dummy::wcl );
    Dummy::wcl.style = CS_OWNDC;
    Dummy::wcl.lpfnWndProc = DummyGLWndProc;
    Dummy::wcl.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle( nullptr ));
    Dummy::wcl.lpszClassName = "DummyGLWindowClass";

    if (!RegisterClassEx( &Dummy::wcl ))
    {
        OutputDebugStringA( "Failed to register dummy window class!\n" );
    }

    Dummy::hwnd = CreateWindow( Dummy::wcl.lpszClassName, "", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, 0, 0, Dummy::wcl.hInstance, 0 );

    Dummy::hdc = GetDC( Dummy::hwnd );

    PIXELFORMATDESCRIPTOR pfd = { 0 };

    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cDepthBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;

    int pf = ChoosePixelFormat( Dummy::hdc, &pfd );

    if (!SetPixelFormat( Dummy::hdc, pf, &pfd ))
    {
        OutputDebugStringA( "Failed to set dummy window pixel format!\n" );
    }

    Dummy::hrc = wglCreateContext( Dummy::hdc );

    if (!wglMakeCurrent( Dummy::hdc, Dummy::hrc ))
    {
        OutputDebugStringA( "Failed to make current dummy OpenGL context!\n" );
    }
}

void DestroyDummyGLWindow()
{
    if (Dummy::hwnd)
    {
        PostMessage( Dummy::hwnd, WM_CLOSE, 0, 0 );

        BOOL bRet;
        MSG msg;

        while ((bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }

    UnregisterClass( Dummy::wcl.lpszClassName, Dummy::wcl.hInstance );
}

void ChooseAntiAliasingPixelFormat( int& outPixelFormat, int samples )
{
    const int attributes[] =
    {
        WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
        WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
        WGL_COLOR_BITS_ARB, 24,
        WGL_ALPHA_BITS_ARB, 8,
        WGL_DEPTH_BITS_ARB, 24,
        WGL_STENCIL_BITS_ARB, 8,
        WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
        WGL_SAMPLE_BUFFERS_ARB, samples > 0 ? 1 : 0,
        WGL_SAMPLES_ARB, samples,
        WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
        0, 0
    };

    PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress( "wglChoosePixelFormatARB" );
    int returnedPixelFormat = 0;
    UINT numFormats = 0;
    BOOL status = wglChoosePixelFormatARB( Dummy::hdc, attributes, 0, 1, &returnedPixelFormat, &numFormats );

    if (status != FALSE && numFormats > 0)
    {
        outPixelFormat = returnedPixelFormat;
    }
}

void CreateRenderer( int samples )
{
    PIXELFORMATDESCRIPTOR pfd = { 0 };
    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_COMPOSITION;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.cDepthBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;

    CreateDummyGLWindow();
    int pixelFormat = 0;
    ChooseAntiAliasingPixelFormat( pixelFormat, samples );
    PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress( "wglCreateContextAttribsARB" );
    DestroyDummyGLWindow();

    if (!SetPixelFormat( WindowGlobal::hdc, pixelFormat, &pfd ))
    {
        OutputDebugStringA( "Failed to set pixel format!\n" );
    }

    int otherBits = 0;

    const int contextAttribs[] =
    {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
        WGL_CONTEXT_MINOR_VERSION_ARB, 1,
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | otherBits,
        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
        0
    };

    HANDLE context = wglCreateContextAttribsARB( WindowGlobal::hdc, 0, contextAttribs );

    if (!wglMakeCurrent( WindowGlobal::hdc, (HGLRC)context ))
    {
        OutputDebugStringA( "Failed to activate forward compatible context!\n" );
    }
}

Edit to answer the SDL question:

SDL makes all this huge amount code to shorten into several lines and additionally the same code works on Windows, Linux, macOS and other OSs. It's well maintained and funded by Valve etc. and has been around for a long time. It also includes input handling code etc.

\$\endgroup\$
4
  • \$\begingroup\$ so basically glew just makes it easier \$\endgroup\$ – Finnick Odair Sep 9 '16 at 10:06
  • \$\begingroup\$ Yes, the process of you yourself loading all the necessary functions is very tedious. \$\endgroup\$ – Tyyppi_77 Sep 9 '16 at 10:08
  • 1
    \$\begingroup\$ @FinnickOdair That's why people usually use a library for function pointer loading, like glew, gl3w etc. \$\endgroup\$ – SurvivalMachine Sep 9 '16 at 10:09
  • 1
    \$\begingroup\$ Also, as a side note, by doing this yourself, you kinda lose the cross-platform advantage that OpenGL gives you, as you now need to port your initialization code over all platforms. \$\endgroup\$ – Tyyppi_77 Sep 9 '16 at 10:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.