基于Win32的OpenGL1.0
Dionysen

使用win32原生API实现OpenGL,可以取代glut等第三方库。

引自此博客

// 使用win32原生API实现OpenGL
#include<Windows.h>
#include<gl/GL.h>

#pragma comment(lib, "opengl32.lib")

BOOL win32_regist_class(const char* class_name) // 注册win32窗口类
{
WNDCLASSEXA cs =
{
sizeof(WNDCLASSEXA),
CS_HREDRAW | CS_VREDRAW,
[](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)->LRESULT
{
switch (msg) {
case WM_NCCREATE:
{
SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCTA)lparam)->lpCreateParams);
break;
};
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
break;
}
case WM_ERASEBKGND:
{
return TRUE;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
break;
}
return DefWindowProcA(hwnd, msg, wparam, lparam);
},
0,
0,
(HINSTANCE)GetModuleHandleA(nullptr),
nullptr,
LoadCursorA(nullptr,IDC_ARROW),
(HBRUSH)COLOR_WINDOW,
nullptr,
class_name,
nullptr
};
return RegisterClassExA(&cs);
}

// 创建win32窗口
HWND win32_create(const char* class_name, const char* window_name, int x, int y, int width, int height)
{
return CreateWindowExA(0, class_name, window_name, WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, width, height, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
}

//获取可用于OpenGL绘制的DC
HDC win32_get_gl_dc(HWND hwnd)
{
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
auto hdc = GetDC(hwnd);
auto pixelFormat = ChoosePixelFormat(hdc, &pfd);
if (!pixelFormat)
{
ReleaseDC(hwnd, hdc);
return nullptr;
}
if (!SetPixelFormat(hdc, pixelFormat, &pfd))
{
ReleaseDC(hwnd, hdc);
return nullptr;
}
return hdc;
}

// 处理win32的消息
BOOL win32_peek_message(HWND hwnd)
{
MSG msg;
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
return FALSE;
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return TRUE;
}

// 创建OpenGL的API
HGLRC gl_create(HDC hdc)
{
auto hglrc = wglCreateContext(hdc);
if (!hglrc)
return nullptr;
if (!wglMakeCurrent(hdc, hglrc))
{
return nullptr;
}
// 这里只获取了glCreateShader作为例子
auto glCreateShader = (GLuint(__stdcall*)(GLenum type))wglGetProcAddress("glCreateShader");
return hglrc;
}


// 渲染
void render(HWND hwnd, HDC hdc, HGLRC hglrc)
{
RECT rc;
GetClientRect(hwnd, &rc);
wglMakeCurrent(hdc, hglrc);
glViewport(0, 0, rc.right, rc.bottom);
glClearColor(0.4f, 0.5f, 0.4f, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glBegin(GL_TRIANGLE_STRIP);
glColor3f(1, 0, 0); glVertex2f(0.0f, 0.0f);
glColor3f(0, 1, 0); glVertex2f(0.5f, 0.5f);
glColor3f(0, 0, 1); glVertex2f(0.0f, 0.5f);
glEnd();
}

int main()
{
win32_regist_class("test");
auto hwnd = win32_create("test", "test", 100, 100, 500, 400);
auto hdc = win32_get_gl_dc(hwnd);
auto hglrc = gl_create(hdc);
while (true)
{
if (win32_peek_message(nullptr) == FALSE)
break;
render(hwnd, hdc, hglrc);
SwapBuffers(wglGetCurrentDC());
}
return 0;
}

基于Win32的OpenGL开发

win32程序的框架是winMain+WndProc,前者创建窗口同时实现窗口循环,后者实现消息处理。

如何在win32下使用OpenGL,窗口创建的过程中,哪一步加入OpenGL的扩展呢?

窗口创建时,由WM_CREATEWM_SIZEWM_PAINT三个消息依次产生,在WM_CREATE中增加窗口对OpenGL支持的代码,同时对OpenGL渲染环境做初始化,然后在WM_SIZE中实现OpenGL视图的变换,这样OpenGL绘图的前置工作就已经做完了,在WM_PAINT中就可以进行渲染了。

❗按照此博客的方法和代码跑不通

另一个思路是,在窗口创建完成时创建OpenGL的渲染环境,在消息循环中渲染(此时的消息循环等同于OpenGL的窗口循环),然后在消息处理函数中处理WM_SIZE来适应窗口大小:(引自基于Win32的OpenGL开发初探

#include <windows.h>
#include<GL/glut.h>

void drawScene(HDC* hdc);
void EnableOpenGL(HWND hWnd, HDC* hdc, HGLRC* hrc);
void DisableOpenGL(HWND hWnd, HDC hdc, HGLRC hrc);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //窗口函数声明

int CALLBACK WinMain(HINSTANCE hInstance, //WinMain函数说明
HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

char lpszClassName[] = "Windows"; //窗口类名
char lpszTitle[] = "Win32 Window"; //窗口标题名
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口类型为缺省类型
wndclass.lpfnWndProc = WndProc; //窗口处理函数为WndProc
wndclass.cbClsExtra = 0; //窗口类无扩展
wndclass.cbWndExtra = 0; //窗口实例无扩展
wndclass.hInstance = hInstance; //当前实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //使用缺省图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //窗口采用箭头光标
wndclass.hbrBackground = NULL; //窗口背景为白色
wndclass.lpszMenuName = NULL; //窗口中无菜单
wndclass.lpszClassName = lpszClassName; //窗口类名为'窗口实例'
if (!RegisterClass(&wndclass)) //注册窗口,若失败,则发出声音
{
MessageBeep(0);
return FALSE;
}
//创建窗口操作
HWND hwnd = CreateWindow(
lpszClassName,
lpszTitle, //窗口实例的标题名
WS_OVERLAPPEDWINDOW, //窗口的风格
CW_USEDEFAULT,
CW_USEDEFAULT, //窗口左上角坐标为缺省值
CW_USEDEFAULT,
CW_USEDEFAULT, //窗口的高度和宽度为缺省值
NULL, //此窗口无父窗口
NULL, //此窗口无主菜单
hInstance, //应用程序当前句柄
NULL); //不使用该值
ShowWindow(hwnd, SW_SHOW); //显示窗口
UpdateWindow(hwnd); //绘制用户区

HGLRC hRC; //绘制环境
HDC hDC; //设备环境
EnableOpenGL(hwnd, &hDC, &hRC);

MSG msg = { 0 }; //消息结构
while (GetMessage(&msg, NULL, 0, 0)) //消息循环
{
TranslateMessage(&msg);
DispatchMessage(&msg);
drawScene(&hDC);
}

DisableOpenGL(hwnd, hDC, hRC);
return 0; //程序终止时,将信息返回操作系统
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
RECT rcClient;

switch (message)
{
case WM_CREATE:
{
return 0;
}
break;
case WM_DESTROY:
{
PostQuitMessage(0); //调用该函数发出WM_QUIT消息
}
break;
case WM_SIZE:
{
GetClientRect(hwnd, &rcClient);
int w = rcClient.right - rcClient.left; // 获得客户区宽度
int h = rcClient.bottom - rcClient.top; // 获得客户区高度
glViewport(0, 0, w, h);
}
break;
case WM_TIMER:
{
InvalidateRect(hwnd, NULL, FALSE);
}
break;
default: //缺省消息处理函数
return DefWindowProc(hwnd, message, wParam, lParam);
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

void drawScene(HDC* hdc) {

glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);

glColor4f(1.0f, 0.0f, 0.0f, 0.0f);

glBegin(GL_QUADS);
glVertex3f(-0.6f, -0.6f, 0.0f);
glVertex3f(0.6f, -0.6f, 0.0f);
glVertex3f(0.6f, 0.6f, 0.0f);
glVertex3f(-0.6f, 0.6f, 0.0f);
glEnd();

SwapBuffers(*hdc);

Sleep(1);
}

void EnableOpenGL(HWND hwnd, HDC* hdc, HGLRC* hrc) {

*hdc = GetDC(hwnd);
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int iPixelFormat = ChoosePixelFormat(*hdc, &pfd);
SetPixelFormat(*hdc, iPixelFormat, &pfd);

// create and enable the render context (RC)
*hrc = wglCreateContext(*hdc);
wglMakeCurrent(*hdc, *hrc);
}

void DisableOpenGL(HWND hwnd, HDC hdc, HGLRC hrc) {
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(hrc);
ReleaseDC(hwnd, hdc);
}

运行结果如下:

image

使用Win32调用GL

#include <Windows.h>
#include <gl/GL.h>
#include <iostream>

const unsigned int windowsWidth = 800;
const unsigned int windowsHeight = 600;
bool fullScreen = true;

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

int APIENTRY wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{

char info_log[512]{};
char lpszClassName[] = "Windows";
char lpszTitle[] = "Win32 Window";
WNDCLASS wndclass{};
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = lpszClassName;
if (!RegisterClass(&wndclass))
{
MessageBeep(0);
return FALSE;
}

HWND hwnd = CreateWindow(
lpszClassName,
lpszTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
windowsWidth,
windowsHeight,
NULL,
NULL,
hInstance,
NULL);



// create context
HDC hDC = GetDC(hwnd);
HGLRC hRC{};

if (hDC)
{
int pixelFormat;
PIXELFORMATDESCRIPTOR pixelFormatDesc;

/* initialize bits to 0 */
memset(&pixelFormatDesc, 0, sizeof(PIXELFORMATDESCRIPTOR));
pixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pixelFormatDesc.nVersion = 1;
pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
pixelFormatDesc.cColorBits = 32;
pixelFormatDesc.cAlphaBits = 8;
pixelFormatDesc.cDepthBits = 24;

pixelFormat = ChoosePixelFormat(hDC, &pixelFormatDesc);
if (pixelFormat)
{
if (SetPixelFormat(hDC, pixelFormat, &pixelFormatDesc))
hRC = wglCreateContext(hDC);
}
}
else
{
std::cout << "ERROR::HDC::CREATE_FAILED\n" << info_log << std::endl;
}
if (hRC) { // 待看
wglMakeCurrent(hDC, hRC);
}
else
{
std::cout << "ERROR::HRC::CREATE_FAILED\n" << info_log << std::endl;
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);

//if (!gladLoadGL())
//{
// std::cout << "Could not initialize GLAD \n";
//}
//else {
// std::cout << "OpenGL Version " << GLVersion.major << std::endl;
//}


MSG msg = { 0 };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wglMakeCurrent(hDC, NULL);
wglDeleteContext(hRC);
if(hDC!=NULL)
ReleaseDC(hwnd, hDC);

return 0;
}

static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
LRESULT result = 0;
switch (msg)
{
case WM_PAINT:
PAINTSTRUCT ps;

//render();
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2i(0, 1);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2i(-1, -1);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2i(1, -1);
glEnd();
glFlush();

BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);

break;
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
break;
case WM_KEYDOWN:
/* ESC: Escape */
if (wParam == 27)
PostMessage(hwnd, WM_CLOSE, 0, 0);
/* F11: Fullscreen */
else if (wParam == 122)
{
DWORD style = GetWindowLong(hwnd, GWL_STYLE);
static int windowX, windowY, windowWidth, windowHeight = { 0 };
if (fullScreen) {
RECT rect;
MONITORINFO mi = { sizeof(mi) };
GetWindowRect(hwnd, &rect);
windowX = rect.left;
windowY = rect.top;
windowWidth = rect.right - rect.left;
windowHeight = rect.bottom - rect.top;
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &mi);
SetWindowLong(hwnd, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
}
else
{
MONITORINFO mi = { sizeof(mi) };
UINT flags = SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW;
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &mi);
SetWindowLong(hwnd, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_NOTOPMOST, windowX, windowY, windowWidth, windowHeight, flags);
}
fullScreen = !fullScreen;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
PostQuitMessage(0);
break;
default:
result = DefWindowProc(hwnd, msg, wParam, lParam);
}
return result;
}
显示评论