正文:
话不多说,直接上代码:
#include<Windows.h>
#include<GL/GL.h>
#pragma comment(lib,"opengl32.lib")
//注册win32窗口类
BOOL win32_regist_class(const char* class_name)
{
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 (PeekMessageA(&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;
//******获取高版本API(glew的最主要功能就是这是一步)
/*
在这个里获取会用到的opengl api,这些API名称可以去opengl官网查询
因为需要的功能不复杂,不需要使用着色器,GL1.0版本的API足够用了。
所以这里并没有获取所有高版本的API,只获取了一个glCreateShader作为例子。
*/
auto glCreateShader=(GLuint(__stdcall*)(GLenum type))wglGetProcAddress("glCreateShader");
//******获取高版本API结束
return hglrc;
}
//渲染
void render(HWND hwnd, HDC hdc, HGLRC hglrc)
{
RECT rc;
GetClientRect(hwnd, &rc);
wglMakeCurrent(hdc, hglrc);//关联gl的DC到窗口的hdc。如果是单DC,只需关联一次即可
glViewport(0, 0, rc.right, rc.bottom);//设置视口
glClearColor(0.4f, 0.5f, 0.4f, 0);//设置清屏时的颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清屏。这里清掉颜色缓冲区与深度缓冲区
//******以下内容为1.0版本绘制方法,高版本的不建议这样绘制了,尽量使用glDraw*系列函数以提高性能
//设置点
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();
//glDrawArrays(GL_LINES, 0, 2);//经过测试,1.0版本不需要调用此函数
}
int main(int argc, const char* argv[])
{
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());//翻转缓冲区(因为使用了双缓冲,必须要翻转才能看到绘制结果)
}
}
整个代码约140行,不算多。这是上次QQ群里一个人问我之后我改了之前的代码写出来的,逻辑比之前强很多,个人感觉优势在于不再需要乱七八糟的第三方库,可以作为学习用,在自己获取高版本GL的API后也可以简单取代glut。
主要部分是win32_get_gl_dc(HWND hwnd)这个函数。这个函数执行成功了就可以继续后续的OpenGL操作,不然的话就会出现GL上下文创建失败的情况,导致后续的OpenGL操作无效。
附:gles32 API获取文件说明
实现我已经放到我的资源里:_gles32.h,直接点击下载就行,只有一个头文件,是从GLES32官网上扒了gles32头文件下来改了实现的。
用法:
- 先调用上面的gl_create函数创建gl环境,不然会像gl_create代码里的注释说的一样接下来的操作失败(glew里包含有这步,所以可以直接用);
gl_load_api(wglGetProcAddress);
至于头文件包含
#include"_gles32.h"
这个不用我多说了吧。
上面的wglGetProcAddress这个就是win32下用来获取高版本GL API的平台函数,之所以放到这里来作为参数传入而不写在_gles32.h里就是希望让_gles32.h平台独立化,所以gles32.h里是没有任何与win32相关的API的,唯一一个相关的已经被放到参数这来了。
实现这个主要是因为原来用的glew还需要下载一个库,研究了下glew发现也没什么特别的,就去gles官网上扒了头文件下来自己改了下就完成实现了。这样下以后就可以不用再去包含一个第三方库。
实际上我还实现了一个_gl_wrapper.h文件来对_gles32.h做包装,里面包含了着色器的操作封装及其他东西,但我觉得这不是OpenGL的核心东西,所以没放到资源里。
完整的调用代码如下:
#include<Windows.h>
#include<GL/GL.h>
//包含头文件
#include"_gles32.h"
#pragma comment(lib,"opengl32.lib")
int main(int argc, const char* argv[])
{
win32_regist_class("test");
auto hwnd = win32_create("test", "test", 100, 100, 500, 400);
auto hdc = win32_get_gl_dc(hwnd);
//哈哈,你没看错,这就是上面那个例子的main函数,就改了这里
auto hglrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hglrc);
//加载高版本GL API(就一个函数)
gl_load_api(wglGetProcAddress);
//加载API结束,接下来就可以正常使用高版本的GLES API了,
//例如glCreateShader、glCreateProgram之类的。
while (true)
{
if(win32_peek_message(nullptr)==FALSE)
break;
render(hwnd, hdc, hglrc);
SwapBuffers(wglGetCurrentDC());//翻转缓冲区(因为使用了双缓冲,必须要翻转才能看到绘制结果)
}
}
(吐槽一下,CSDN的markdown排版有点难用啊,我用typora编辑的时候没这么蛋疼,而且typora和CSDN用的数学公式渲染器不是同一个,导致我在typora上编辑好的公式没法直接复制过来用)