Windows原生API抗锯齿

笔者最近在使用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:

  1. 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.
  2. Use the helper GL context to get the wgl Extension pointers. You should also check if the extensions are available, of course.
  3. Create the real GL context using the WGL extensions, and
  4. 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sumzeek丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值