Last active
April 5, 2022 10:13
-
-
Save yanpeng/b55590c7467f3b3615ce425f1a2a7da1 to your computer and use it in GitHub Desktop.
Create OpenGL Rendering Context from Scratch (Win32)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// To run this program. | |
// 1. SUBSYSTEM:WINDOWS | |
// 2. Link glad.cpp | |
// 3. Set include directories that includes glad.h and wglext.h | |
// https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL) | |
// https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt | |
#include <Windows.h> | |
#include <glad/glad.h> | |
// #include <GL/glext.h> // No use, don't use gl extensions here. | |
#include <GL/wglext.h> | |
#include <stdexcept> | |
#pragma comment(lib, "opengl32.lib") | |
static GLint g_majorVersion; | |
static GLint g_minorVersion; | |
// WGL extensions | |
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; | |
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; | |
static void* GetAnyGLFuncAddress(const char* name); | |
static void LoadWGLExtensions(); | |
static bool CreateFakeWindow(); | |
static bool CreateRealWindow(HWND& hWnd); | |
void FatalError(const TCHAR* msg, const TCHAR* caption) | |
{ | |
MessageBox(NULL, msg, caption, MB_ICONERROR); | |
} | |
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |
int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PSTR szCmdLine, _In_ int iCmdShow) | |
{ | |
if (!CreateFakeWindow()) | |
{ | |
return -1; | |
} | |
HWND hWnd; | |
bool result = CreateRealWindow(hWnd); | |
if (!result) | |
{ | |
return -1; | |
} | |
ShowWindow(hWnd, SW_SHOWNORMAL); | |
SetForegroundWindow(hWnd); | |
HDC hDC = GetDC(hWnd); | |
bool active = true; | |
while (active) | |
{ | |
MSG msg; | |
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) | |
{ | |
if (msg.message == WM_QUIT) | |
{ | |
active = false; | |
} | |
else | |
{ | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
} | |
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT); | |
SwapBuffers(hDC); | |
} | |
return 0; | |
} | |
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (message) | |
{ | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
break; | |
default: | |
return DefWindowProcW(hWnd, message, wParam, lParam); | |
} | |
return 0; | |
} | |
void* GetAnyGLFuncAddress(const char* name) | |
{ | |
void* p = (void*)wglGetProcAddress(name); | |
if (p == 0 || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1)) | |
{ | |
HMODULE module = LoadLibraryA("opengl32.dll"); | |
if (module != NULL) | |
{ | |
p = (void*)GetProcAddress(module, name); | |
} | |
else | |
{ | |
throw std::invalid_argument("Cannot load opengl32.dll."); | |
} | |
} | |
return p; | |
} | |
void LoadWGLExtensions() | |
{ | |
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); | |
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); | |
} | |
bool CreateFakeWindow() | |
{ | |
static TCHAR appName[] = TEXT("Fake Window"); | |
WNDCLASSEX wndClass; | |
memset(&wndClass, 0, sizeof(WNDCLASSEXW)); | |
wndClass.cbSize = sizeof(WNDCLASSEXW); | |
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; | |
wndClass.lpfnWndProc = DefWindowProc; | |
wndClass.hInstance = (HINSTANCE)GetModuleHandleW(NULL); | |
wndClass.lpszClassName = appName; | |
if (!RegisterClassEx(&wndClass)) | |
{ | |
FatalError(TEXT("Cannot register window class for fake window."), appName); | |
return false; | |
} | |
HWND fakeHWnd = CreateWindowEx( | |
0, | |
wndClass.lpszClassName, | |
TEXT("Fake Window"), | |
0, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
NULL, | |
NULL, | |
wndClass.hInstance, | |
NULL); | |
if (!fakeHWnd) | |
{ | |
FatalError(TEXT("Failed to create fake window."), appName); | |
return false; | |
} | |
PIXELFORMATDESCRIPTOR pfd = | |
{ | |
sizeof(PIXELFORMATDESCRIPTOR), | |
1, | |
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags | |
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. | |
32, // Colordepth of the framebuffer. | |
0, 0, 0, 0, 0, 0, | |
0, | |
0, | |
0, | |
0, 0, 0, 0, | |
24, // Number of bits for the depthbuffer | |
8, // Number of bits for the stencilbuffer | |
0, // Number of Aux buffers in the framebuffer. | |
PFD_MAIN_PLANE, | |
0, | |
0, 0, 0 | |
}; | |
HDC fakeHDC = GetDC(fakeHWnd); | |
int pixelFormatIndex = ChoosePixelFormat(fakeHDC, &pfd); | |
if (pixelFormatIndex == 0) | |
{ | |
FatalError(TEXT("Cannot acquire a pixel format."), appName); | |
ReleaseDC(fakeHWnd, fakeHDC); | |
DestroyWindow(fakeHWnd); | |
return false; | |
} | |
if (!SetPixelFormat(fakeHDC, pixelFormatIndex, &pfd)) | |
{ | |
FatalError(TEXT("Cannot set the pixel format."), appName); | |
ReleaseDC(fakeHWnd, fakeHDC); | |
DestroyWindow(fakeHWnd); | |
return false; | |
} | |
HGLRC fakeOpenGLRenderingContext = wglCreateContext(fakeHDC); | |
if (!fakeOpenGLRenderingContext) | |
{ | |
FatalError(TEXT("Failed to create the fake OpenGL context."), appName); | |
ReleaseDC(fakeHWnd, fakeHDC); | |
DestroyWindow(fakeHWnd); | |
return false; | |
} | |
if (!wglMakeCurrent(fakeHDC, fakeOpenGLRenderingContext)) | |
{ | |
wglDeleteContext(fakeOpenGLRenderingContext); | |
FatalError(TEXT("Failed to make current."), appName); | |
ReleaseDC(fakeHWnd, fakeHDC); | |
DestroyWindow(fakeHWnd); | |
return false; | |
} | |
// Use GLAD to load opengl functions(only core here). | |
// GLAD checks OpenGL version internally. | |
gladLoadGLLoader((GLADloadproc)GetAnyGLFuncAddress); | |
LoadWGLExtensions(); | |
glGetIntegerv(GL_MAJOR_VERSION, &g_majorVersion); | |
glGetIntegerv(GL_MINOR_VERSION, &g_minorVersion); | |
//// Check OpenGL extensions here, which method is selected depending on | |
//// the OpenGL version. | |
//// GLint numExtensions; | |
//// glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); | |
//// auto ex0 = (const char*)glGetStringi(GL_EXTENSIONS, 0); | |
//// Check wgl extensions here | |
///*PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB"); | |
//const char* wglExtensions = (const char*)wglGetExtensionsStringARB(hDC);*/ | |
wglDeleteContext(fakeOpenGLRenderingContext); | |
ReleaseDC(fakeHWnd, fakeHDC); | |
DestroyWindow(fakeHWnd); | |
return true; | |
} | |
bool CreateRealWindow(HWND& hWnd) | |
{ | |
static TCHAR appName[] = TEXT("OpenGL Window"); | |
WNDCLASSEX wndClass; | |
memset(&wndClass, 0, sizeof(WNDCLASSEXW)); | |
wndClass.cbSize = sizeof(WNDCLASSEXW); | |
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; | |
wndClass.lpfnWndProc = WndProc; | |
wndClass.hInstance = (HINSTANCE)GetModuleHandleW(NULL); | |
wndClass.hIcon = NULL; | |
wndClass.hIconSm = NULL; | |
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); | |
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); | |
wndClass.lpszClassName = appName; | |
if (!RegisterClassEx(&wndClass)) | |
{ | |
FatalError(TEXT("Cannot register window class for OpenGL window."), appName); | |
return NULL; | |
} | |
MONITORINFO monitorInfo; | |
monitorInfo.cbSize = sizeof(monitorInfo); | |
GetMonitorInfo(MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY), &monitorInfo); | |
long x = monitorInfo.rcMonitor.left; | |
long y = monitorInfo.rcMonitor.top; | |
const long monitorWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; | |
const long monitorHeight = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; | |
DWORD style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; | |
DWORD exStyle = WS_EX_APPWINDOW; | |
RECT rect; | |
SetRect(&rect, 0, 0, 800, 600); | |
AdjustWindowRectEx(&rect, style, FALSE, exStyle); | |
long width = rect.right - rect.left; | |
long height = rect.bottom - rect.top; | |
x += (monitorWidth - width) / 2; | |
y += (monitorHeight - height) / 2; | |
hWnd = CreateWindowEx( | |
exStyle, | |
wndClass.lpszClassName, | |
TEXT("OpenGL Window"), | |
style, | |
x, y, width, height, | |
NULL, | |
NULL, | |
wndClass.hInstance, | |
NULL); | |
if (!hWnd) | |
{ | |
FatalError(TEXT("Failed to create OpenGL window."), appName); | |
return false; | |
} | |
HDC hDC = GetDC(hWnd); | |
const int attribList[] = | |
{ | |
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, | |
WGL_SUPPORT_OPENGL_ARB, GL_TRUE, | |
WGL_DOUBLE_BUFFER_ARB, GL_TRUE, | |
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, | |
WGL_COLOR_BITS_ARB, 32, | |
WGL_DEPTH_BITS_ARB, 24, | |
WGL_STENCIL_BITS_ARB, 8, | |
0, // End | |
}; | |
int pixelFormat; | |
UINT numFormats; | |
UINT maxFormats = 1; | |
wglChoosePixelFormatARB(hDC, attribList, NULL, maxFormats, &pixelFormat, &numFormats); | |
if (numFormats == 0) | |
{ | |
FatalError(TEXT("Cannot get pixel format."), appName); | |
ReleaseDC(hWnd, hDC); | |
DestroyWindow(hWnd); | |
return false; | |
} | |
PIXELFORMATDESCRIPTOR pfd; | |
DescribePixelFormat(hDC, pixelFormat, sizeof(pfd), &pfd); | |
if (!SetPixelFormat(hDC, pixelFormat, &pfd)) | |
{ | |
FatalError(TEXT("Failed to set the pixel format."), appName); | |
ReleaseDC(hWnd, hDC); | |
DestroyWindow(hWnd); | |
return false; | |
} | |
const int contextAttribList[] = | |
{ | |
WGL_CONTEXT_MAJOR_VERSION_ARB, g_majorVersion, | |
WGL_CONTEXT_MINOR_VERSION_ARB, g_minorVersion, | |
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, | |
0 | |
}; | |
HGLRC openglRenderingContext = wglCreateContextAttribsARB(hDC, NULL, contextAttribList); | |
if (!openglRenderingContext) | |
{ | |
FatalError(TEXT("Failed to create OpenGL rendering context."), appName); | |
ReleaseDC(hWnd, hDC); | |
DestroyWindow(hWnd); | |
return false; | |
} | |
if (!wglMakeCurrent(hDC, openglRenderingContext)) | |
{ | |
wglDeleteContext(openglRenderingContext); | |
ReleaseDC(hWnd, hDC); | |
DestroyWindow(hWnd); | |
return false; | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment