Skip to content

Instantly share code, notes, and snippets.

@JustSlavic
Last active June 2, 2025 18:21
Show Gist options
  • Save JustSlavic/e394bd39dce8155b2c260681e9c62c93 to your computer and use it in GitHub Desktop.
Save JustSlavic/e394bd39dce8155b2c260681e9c62c93 to your computer and use it in GitHub Desktop.
The simplest single file OpenGL render example only win32 without libraries
#include <stdint.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <GL/gl.h>
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
typedef const char *WINAPI wglGetExtensionsStringARBType(HDC hdc);
typedef BOOL WINAPI wglChoosePixelFormatARBType(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
typedef HGLRC WINAPI wglCreateContextAttribsARBType(HDC hdc, HGLRC hShareContext, int const *attribList);
typedef BOOL wglSwapIntervalEXTType(int interval);
typedef int wglGetSwapIntervalEXTType(void);
static wglGetExtensionsStringARBType *wglGetExtensionsStringARB;
static wglChoosePixelFormatARBType *wglChoosePixelFormatARB;
static wglCreateContextAttribsARBType *wglCreateContextAttribsARB;
static wglSwapIntervalEXTType *wglSwapIntervalEXT;
static wglGetSwapIntervalEXTType *wglGetSwapIntervalEXT;
#define GL_DEPTH_STENCIL 0x84F9
#define GL_ARRAY_BUFFER 0x8892
#define GL_STATIC_DRAW 0x88E4
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
/* ClearBufferMask */
#define GL_DEPTH_BUFFER_BIT 0x00000100
#define GL_STENCIL_BUFFER_BIT 0x00000400
#define GL_COLOR_BUFFER_BIT 0x00004000
/* Boolean */
#define GL_FALSE 0
#define GL_TRUE 1
/* DataType */
#define GL_FLOAT 0x1406
typedef char GLchar;
typedef long long GLsizeiptr;
typedef void glGenBuffersType(GLsizei n, GLuint *buffers);
typedef void glBindBufferType(GLenum target, GLuint buffer);
typedef void glBufferDataType(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void glGenVertexArraysType(GLsizei n, GLuint *arrays);
typedef void glBindVertexArrayType(GLuint array);
typedef void glVertexAttribPointerType(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
typedef void glEnableVertexAttribArrayType(GLuint index);
typedef GLuint glCreateShaderType(GLenum shaderType);
typedef void glShaderSourceType(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
typedef void glCompileShaderType(GLuint shader);
typedef GLuint glCreateProgramType(void);
typedef void glAttachShaderType(GLuint program, GLuint shader);
typedef void glDetachShaderType(GLuint program, GLuint shader);
typedef void glLinkProgramType(GLuint program);
typedef void glUseProgramType(GLuint program);
typedef void glGetShaderivType(GLuint shader, GLenum pname, GLint *params);
static glGenBuffersType *glGenBuffers;
static glBindBufferType *glBindBuffer;
static glBufferDataType *glBufferData;
static glGenVertexArraysType *glGenVertexArrays;
static glBindVertexArrayType *glBindVertexArray;
static glVertexAttribPointerType *glVertexAttribPointer;
static glEnableVertexAttribArrayType *glEnableVertexAttribArray;
static glCreateShaderType *glCreateShader;
static glShaderSourceType *glShaderSource;
static glCompileShaderType *glCompileShader;
static glCreateProgramType *glCreateProgram;
static glAttachShaderType *glAttachShader;
static glDetachShaderType *glDetachShader;
static glLinkProgramType *glLinkProgram;
static glUseProgramType *glUseProgram;
static glGetShaderivType *glGetShaderiv;
bool initialize_opengl()
{
#define STRINGIFY_(X) #X
#define STRINGIFY(X) STRINGIFY_(X)
#define WGL_GET_PROC_ADDRESS(NAME) NAME = (NAME##Type *) wglGetProcAddress(STRINGIFY(NAME)); \
if (NAME == NULL) return false;
WGL_GET_PROC_ADDRESS(glGenBuffers);
WGL_GET_PROC_ADDRESS(glBindBuffer);
WGL_GET_PROC_ADDRESS(glBufferData);
WGL_GET_PROC_ADDRESS(glGenVertexArrays);
WGL_GET_PROC_ADDRESS(glBindVertexArray);
WGL_GET_PROC_ADDRESS(glVertexAttribPointer);
WGL_GET_PROC_ADDRESS(glEnableVertexAttribArray);
WGL_GET_PROC_ADDRESS(glCreateShader);
WGL_GET_PROC_ADDRESS(glShaderSource);
WGL_GET_PROC_ADDRESS(glCompileShader);
WGL_GET_PROC_ADDRESS(glCreateProgram);
WGL_GET_PROC_ADDRESS(glAttachShader);
WGL_GET_PROC_ADDRESS(glDetachShader);
WGL_GET_PROC_ADDRESS(glLinkProgram);
WGL_GET_PROC_ADDRESS(glUseProgram);
WGL_GET_PROC_ADDRESS(glGetShaderiv);
#undef WGL_GET_PROC_ADDRESS
return true;
}
int get_width(RECT rectangle)
{
int result = rectangle.right - rectangle.left;
return result;
}
int get_height(RECT rectangle)
{
int result = rectangle.bottom - rectangle.top;
return result;
}
struct window
{
HWND Handle;
HDC DeviceContext;
};
#define MAIN_WINDOW_CALLBACK(NAME) LRESULT CALLBACK NAME(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
typedef MAIN_WINDOW_CALLBACK(MainWindowCallbackType);
static bool is_running;
static uint32_t current_client_width;
static uint32_t current_client_height;
window create_window(HINSTANCE Instance, int ClientWidth, int ClientHeight, MainWindowCallbackType *WindowCallback)
{
int PrimaryMonitorWidth = GetSystemMetrics(SM_CXSCREEN);
int PrimaryMonitorHeight = GetSystemMetrics(SM_CYSCREEN);
HBRUSH BlackBrush = CreateSolidBrush(RGB(0, 0, 0));
WNDCLASSA WindowClass = {};
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.lpfnWndProc = WindowCallback;
WindowClass.hInstance = Instance;
WindowClass.lpszClassName = "WindowClass";
WindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WindowClass.hbrBackground = BlackBrush;
ATOM WindowClassAtom = RegisterClassA(&WindowClass);
RECT WindowRectangle = { 0, 0, (int) ClientWidth, (int) ClientHeight };
AdjustWindowRect(&WindowRectangle, WS_OVERLAPPEDWINDOW, false);
HWND WindowHandle = CreateWindowExA(0, WindowClass.lpszClassName, "OpenGL temp window", WS_OVERLAPPEDWINDOW, 0, 0, 50, 50, 0, 0, Instance, NULL);
HDC DeviceContext = GetDC(WindowHandle);
HGLRC TempRenderContext = {};
HGLRC RenderContext = {};
PIXELFORMATDESCRIPTOR desired_pixel_format = {};
desired_pixel_format.nSize = sizeof(desired_pixel_format);
desired_pixel_format.nVersion = 1;
desired_pixel_format.dwFlags = PFD_SUPPORT_OPENGL|PFD_DRAW_TO_WINDOW|PFD_DOUBLEBUFFER;
desired_pixel_format.iPixelType = PFD_TYPE_RGBA;
desired_pixel_format.cColorBits = 24;
desired_pixel_format.cAlphaBits = 8;
desired_pixel_format.cDepthBits = 24;
desired_pixel_format.cStencilBits = 8;
desired_pixel_format.iLayerType = PFD_MAIN_PLANE;
int suggested_pixel_format_index = ChoosePixelFormat(DeviceContext, &desired_pixel_format);
PIXELFORMATDESCRIPTOR suggested_pixel_format;
DescribePixelFormat(DeviceContext, suggested_pixel_format_index, sizeof(suggested_pixel_format), &suggested_pixel_format);
if (SetPixelFormat(DeviceContext, suggested_pixel_format_index, &suggested_pixel_format))
{
TempRenderContext = wglCreateContext(DeviceContext);
if (wglMakeCurrent(DeviceContext, TempRenderContext))
{
wglGetExtensionsStringARB = (wglGetExtensionsStringARBType *) wglGetProcAddress("wglGetExtensionsStringARB");
wglChoosePixelFormatARB = (wglChoosePixelFormatARBType *) wglGetProcAddress("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (wglCreateContextAttribsARBType *) wglGetProcAddress("wglCreateContextAttribsARB");
// @todo Check if 'WGL_EXT_swap_control' extension is available
wglSwapIntervalEXT = (wglSwapIntervalEXTType *) wglGetProcAddress("wglSwapIntervalEXT");
wglGetSwapIntervalEXT = (wglGetSwapIntervalEXTType *) wglGetProcAddress("wglGetSwapIntervalEXT");
wglMakeCurrent(NULL, NULL);
wglDeleteContext(TempRenderContext);
ReleaseDC(WindowHandle, DeviceContext);
DestroyWindow(WindowHandle);
WindowHandle = CreateWindowExA(
0, WindowClass.lpszClassName,
"window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
(PrimaryMonitorWidth - get_width(WindowRectangle)) / 2,
(PrimaryMonitorHeight - get_height(WindowRectangle)) / 2,
get_width(WindowRectangle),
get_height(WindowRectangle),
0, 0, Instance, NULL);
DeviceContext = GetDC(WindowHandle);
int wgl_attribute_list[] =
{
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
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,
WGL_SAMPLE_BUFFERS_ARB, 1,
WGL_SAMPLES_ARB, 4,
0, // End
};
int pixel_format;
uint32_t number_formats;
wglChoosePixelFormatARB(DeviceContext, wgl_attribute_list, NULL, 1, &pixel_format, &number_formats);
SetPixelFormat(DeviceContext, pixel_format, &desired_pixel_format);
int wgl_context_attrib_list[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0, // End
};
RenderContext = wglCreateContextAttribsARB(DeviceContext, 0, wgl_context_attrib_list);
wglMakeCurrent(DeviceContext, RenderContext);
}
}
window Result;
Result.Handle = WindowHandle;
Result.DeviceContext = DeviceContext;
return Result;
}
MAIN_WINDOW_CALLBACK(window_callback)
{
LRESULT result = {};
switch (message)
{
case WM_SIZE:
current_client_width = LOWORD(lParam);
current_client_height = HIWORD(lParam);
break;
case WM_CLOSE:
case WM_DESTROY:
is_running = false;
break;
default:
result = DefWindowProcA(window, message, wParam, lParam);
}
return result;
}
void process_pending_messages()
{
MSG message;
while (PeekMessageA(&message, 0, 0, 0, PM_REMOVE))
{
if (message.message == WM_QUIT) is_running = false;
TranslateMessage(&message);
DispatchMessageA(&message);
}
}
char const *vertex_shader_text = R"GLSL(
#version 410
layout (location = 0) in vec2 vertex_position;
layout (location = 1) in vec3 vertex_color;
out vec4 fragment_color;
void main()
{
fragment_color = vec4(vertex_color, 1.0);
gl_Position = vec4(vertex_position.x, vertex_position.y, 0.0, 1.0);
}
)GLSL";
char const *fragment_shader_text = R"GLSL(
#version 410
in vec4 fragment_color;
out vec4 result_color;
void main()
{
result_color = fragment_color;
}
)GLSL";
int WinMain(HINSTANCE Instance, HINSTANCE PrevInstance, LPSTR CmdLine, int ShowCode)
{
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
window w = create_window(Instance, 1600, 900, window_callback);
initialize_opengl();
glClearColor(0.f, 0.f, 0.f, 1.f);
wglSwapIntervalEXT(1);
uint32_t vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader_id, 1, (char const **) &vertex_shader_text, (int const *) NULL);
glCompileShader(vertex_shader_id);
int successful;
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &successful);
uint32_t fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader_id, 1, (char const **) &fragment_shader_text, (int const *) NULL);
glCompileShader(fragment_shader_id);
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &successful);
int shader_id = glCreateProgram();
glAttachShader(shader_id, vertex_shader_id);
glAttachShader(shader_id, fragment_shader_id);
glLinkProgram(shader_id);
float vertices[] =
{
// positions // colors
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f
};
uint32_t vao_id, vbo_id;
glGenBuffers(1, &vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao_id);
glBindVertexArray(vao_id);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) (2 * sizeof(float)));
is_running = true;
while (is_running)
{
process_pending_messages();
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_id);
glBindVertexArray(vao_id);
glDrawArrays(GL_TRIANGLES, 0, 6);
SwapBuffers(w.DeviceContext);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment