用GLFW+GLAD库已经好一段时间了。但是我还是觉得GLFW不如原生的win32窗口用着顺手,毕竟win32窗口可以自绘加控件等等,可玩性可谓是很高的。
微软给OpenGL留了套东西,不过可惜,哪怕到了Windows 10,这套API都停留在OpenGL 1.x时代。甚至还是用的CPU渲染。新版本的API藏在显卡开发商提供的程序中,我们需要拿到这些API。
说到这里应该你就熟悉了,这些API有一个库可以帮忙——GLAD。而GLAD通过一个回调来拿到这些函数的地址,这个函数类型(我还是愿意给函数一个类型,正如大部分编译器的类型系统实现那样,因此不纠结细节了~)是GLADloadproc,是gladLoadGLLoader函数唯一的参数(截止至今天,2020-07-13)。了解这些之后,我们需要做的问题就是两个:一是实现这个函数,一个是初始化wgl一系列相关的。我们分步来,下面,假如你已经准备好了一个空窗口,现在我们开始干活吧!
WGL是建立在HDC上的,因此,第一步我们需要拿到HDC。而dll其实是为了方便glad,这个一会会说。
hDC = GetDC(hWnd);
glInst = LoadLibraryA("opengl32.dll");
if (glInst == NULL)
{
return false;
}
一般而言这个调用是成功的,只要窗口有效就可以了,加载的dll和动态调用dll的操作是一样的。
如大多数教程,我们需要设置一下HDC希望匹配的像素格式。现在的OpenGL已经不需要拘于太多细节,因此直接这样就可以做到:
bool selectHDCFormat(HDC hdc) noexcept
{
PIXELFORMATDESCRIPTOR pfd = { 0 };
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
auto pxfmt = ChoosePixelFormat(hdc, &pfd);
if (pxfmt == 0) // 不支持的像素格式
{
return false;
}
auto _ = SetPixelFormat(hdc, pxfmt, &pfd);
assert(_);
return true;
}
调用这个函数,如果返回false则表示失败。我们打开了双缓冲,这是因为双缓冲能有效减少绘制过程带来的闪烁。
HDC准备好后,我们需要创建一个HGLRC,然后把这个HGLRC绑定给HDC:
hRC = wglCreateContext(hDC);
if (wglMakeCurrent(hDC, hRC) == FALSE)
{
return false;
}
这一步弄完就可以了。查一下GLADloadproc长什么样,它在glad.h里面可以看到:
typedef void* (* GLADloadproc)(const char *name);
很显然,它将一个函数名作为输入,返回这个函数的地址。而注意,这个不单单是只取得OpenGL拓展,还包括OpenGL基本的API,如glGetString。在OpenGL 1.x时代还没有拓展的概念,因此我们需要去dll里面动态加载它,得到这个函数的地址。这就是之前使用LoadLibrary的原因。好了,所以这个函数应该先调用wglGetProcAddress,如果失败则调用GetProcAddress:
static void* cWGLGetProcAddr(const char *name)
{
auto ret = wglGetProcAddress(name);
if (ret == NULL)
{
ret = GetProcAddress(glModleInst, name);
}
return ret;
}
接着,在你初始化glad的地方这样写:
bool initgladFuncAddr() noexcept
{
assert(wglGetCurrentContext() != NULL); // 保证wgl有合适的上下文
glModleInst = glInst;
if (gladLoadGLLoader(cWGLGetProcAddr) == 0)
{
return false;
}
return true;
}
这样就大功告成了!运行一下程序,试试glGetString(GL_VERSION)的结果是什么呢?
我们现在可以使用glEnable();操作一些东西,例如glEnable(GL_DEPTH_TEST);。但是有个地方没实现:垂直同步。
垂直同步是OpenGL的拓展,所以可以这样折腾:
typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int);
PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress("wglSwapIntervalEXT");
assert(wglSwapIntervalEXT != NULL);
这样的话,就可以用wglSwapIntervalEXT(1);来打开垂直同步,用wglSwapIntervalEXT(0);来关闭它。到这里是确实结束了,上一下我自己做出来的效果。注意窗口,很明显这个窗口和GLFW的( WS_OVERLAPPEDWINDOW那样的 )有差异:
最后不要忘记用ReleaseDC、FreeLibrary、wglDeleteContext释放掉我们的资源