笔者最近在使用windows原生API进行OpenGL项目开发时,遇到了一些抗锯齿相关的问题,在此记录总结以帮助遇到类似问题的同仁。
相信查阅了很多资料的读者都知道,windows想要打开抗锯齿需要用到wglChoosePixelFormatARB
函数,这是windows官方给我们提供的API,但是如何正确使用这个函数却很有讲究。
我们知道windows创建opengl上下文时,需要先设置像素格式(pixel format),也就是如下所示,在wglCreateContext(hdc)
前必须设置像素格式SetPixelFormat(hdc, pixelFormat, &pfd)
hdc = GetDC(hwnd);
// set pixel format to create opengl context
PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32 };
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
// create opengl render context
hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hrc);
if (!gladLoadGL()) {
std::cout << "Failed to initialize GLAD" << std::endl;
}
而抗锯齿就是通过设置对应的像素格式WGL_SAMPLES_ARB
来开启,到此为止都很顺理成章,然而关键就在于wglChoosePixelFormatARB
,这个函数指针必须在创建了OpenGL上下文之后才可以调用,但是我们知道想要创建OpenGL上下文就需要先设置像素格式,而抗锯齿的像素格式需要创建了OpenGL上下文之后才能获取,这就成了鸡生蛋,单生鸡的死锁问题。
int attributes[] = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 24,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, 4, // 4x anti-alias
0
};
int pixelFormat;
UINT numFormats;
wglChoosePixelFormatARB(hdc, attributes, nullptr, 1, &pixelFormat, &numFormats);
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
为了解决这个问题,我查阅了相关资料后采取的方法为先创建一个临时窗口(助手窗口),通过这个临时窗口来获取wglChoosePixelFormatARB
的函数指针,再通过该函数指针在实际的窗口中创建抗锯齿像素格式。
On windows, what you actually have to do is the following:
- Create a “helper” GL context with (as much as possible) the same attributes as the GL context you actually want to create. Note that GL and WGL extnesions will depend on the GL context you create on windows.
- Use the helper GL context to get the wgl Extension pointers. You should also check if the extensions are available, of course.
- Create the real GL context using the WGL extensions, and
- destroy the helper context.
具体的代码如下:
typedef BOOL (APIENTRY *PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
bool OpenGLRenderer::get_wglChoosePixelFormatARB_proc()
{
// Step 1: Create a simple window
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = TEXT("TempWindowClass");
RegisterClass(&wc);
HWND temphwnd = CreateWindow(wc.lpszClassName, TEXT("TempWindowClass"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, wc.hInstance, NULL);
// Step 2: Set up OpenGL context
HDC temphdc = GetDC(temphwnd);
PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32 };
int pixelFormat = ChoosePixelFormat(temphdc, &pfd);
SetPixelFormat(temphdc, pixelFormat, &pfd);
HGLRC temphrc = wglCreateContext(temphdc);
wglMakeCurrent(temphdc, temphrc);
// Step 3: Use wglChoosePixelFormatARB
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
// Step 4: Cleanup
wglMakeCurrent(NULL, NULL);
wglDeleteContext(temphrc);
ReleaseDC(temphwnd, temphdc);
DestroyWindow(temphwnd);
if (!wglChoosePixelFormatARB) {
return true;
}
return false;
}
void OpenGLRenderer::initOpenGL_WIN32()
{
hdc = GetDC(hwnd);
// tring to enable anti-alias
if (get_wglChoosePixelFormatARB_proc()) {
MessageBox( NULL, "wglChoosePixelFormatARB not supported", "Error! (SetupWGLPixelFormat)", MB_OK );
std::cout << "Unable to enable anti aliasing option!" << std::endl;
PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32 };
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
} else {
int attributes[] = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 24,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, 4, // 4x anti-alias
0
};
int pixelFormat;
UINT numFormats;
wglChoosePixelFormatARB(hdc, attributes, nullptr, 1, &pixelFormat, &numFormats);
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(hdc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
}
// create opengl render context
hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hrc);
if (!gladLoadGL()) {
std::cout << "Failed to initialize GLAD" << std::endl;
}
// get extension list
int numExtensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
for (int i = 0; i < numExtensions; ++i) {
const char *extension = (const char *) glGetStringi(GL_EXTENSIONS, i);
// trun off the VSYNC
if (strcmp(extension, "WGL_EXT_swap_control") == 0) {
wglSwapIntervalEXT = (PFNWGLEXTSWAPCONTROLPROC) wglGetProcAddress("wglSwapIntervalEXT");
wglGetSwapIntervalEXT = (PFNWGLEXTGETSWAPINTERVALPROC) wglGetProcAddress("wglGetSwapIntervalEXT");
wglSwapIntervalEXT(0); // DisableVSync
}
}
}
参考资料:
Creating an OpenGL Context (WGL)
OpenGL Anti-aliasing using GLEW with WGL
WINDOWS下,通过程序设置全屏抗锯齿(多重采样)的方法
wglChoosePixelFormatARB