65.9K
CodeProject 正在变化。 阅读更多。
Home

如何为 ATL 控件添加 OpenGL 支持

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (7投票s)

2002年9月29日

CPOL

2分钟阅读

viewsIcon

114238

downloadIcon

1370

一篇描述将基本 OpenGL 支持添加到 ATL 控件的逐步过程的文章

引言

本教程将演示如何向 ATL 控件添加 OpenGL 支持,并且构成了一个精简的、有组织的 Visual C++ 附带的 ATL OpenGL 演示版本,以更易于打印和理解的格式呈现。 要使用本教程,您必须在项目中创建一个 ATL 控件。 以下是向 ATL 控件添加最基本的 OpenGL 支持的分步指南。

注意:虽然我在函数声明前加上了 CMyControl:: 前缀,但我建议将这些函数内联(在头文件中)编写,如果这样做,则必须删除前缀。

  1. 添加对头文件 <gl/gl.h> 和 <gl/glu.h> 的支持;添加对 opengl32.libglu32.lib 库的支持。

    这可以通过在 stdafx.h 文件的末尾添加以下四行代码来完成

    #include <gl/gl.h>
    #include <gl/glu.h>
    #pragma comment(lib, "opengl32.lib")
    #pragma comment(lib, "glu32.lib")
    
  2. 将 OpenGL 渲染上下文变量添加到您的控件类
    // OpenGL rendering context
    HGLRC m_hRC;
    
    现在创建一个函数来选择 OpenGL 像素格式。 此函数将设备上下文句柄作为参数,并将专门为此设备上下文设置像素格式。 该函数应如下所示
    // Set OpenGL pixel format for given DC
    BOOL MyControl::SetupPixelFormat(HDC hdc)
    {
        static 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 pixelformat;
    
        if ((pixelformat = ChoosePixelFormat(hdc, &pfd)) == 0)
        {
            ATLASSERT(FALSE);
            return FALSE;
        }
    
        if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE)
        {
            ATLASSERT(FALSE);
            return FALSE;
        }
    
        return TRUE;
    }
  3. 创建一个将创建 OpenGL 渲染上下文并初始化视口的函数。 请注意,在设置当前渲染上下文之后,我们可以开始调用 OpenGL 函数,例如 glViewport
    // Create rendering context given device context and control bounds
    void MyControl::CreateContext(HDC hdc, RECT& rc)
    {
        PIXELFORMATDESCRIPTOR pfd;
        if (!SetupPixelFormat(hdc))
            return;
    
        int n = GetPixelFormat(hdc);
        DescribePixelFormat(hdc, n, sizeof(pfd), &pfd);
        m_hRC = wglCreateContext(hdc);
        wglMakeCurrent(hdc, m_hRC);
    
        // OpenGL code starts here - below is an example
        int width = rc.right - rc.left;
        int height = rc.bottom - rc.top;
    
        glViewport(0, 0, width, height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(-width/2, width/2, -height/2, height/2);
        glMatrixMode(GL_MODELVIEW);
    }
  4. OnCreate 添加对上述函数的调用。 为此,您首先必须创建一个 WM_CREATE 处理程序。 使用“属性”工具栏上的“消息”按钮添加处理程序。 然后,找到该函数并添加类似于以下代码
    LRESULT CMyControl::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/,
        LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        HDC hdc = GetDC();
        RECT rc;
        GetClientRect(&rc);
        CreateContext(hdc, rc);
    
        return 0;
    }
  5. 以类似的方式,为 OnDestroy (WM_DESTROY) 添加代码以清除渲染上下文并删除它。
    LRESULT CMyControl::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
      wglMakeCurrent(NULL, NULL);
    
      if (m_hRC)
      {
        wglDeleteContext(m_hRC);
        m_hRC = NULL;
      }
    
      return 0;
    }
    
  6. 处理 WM_SIZE 也很重要。 每当窗口大小调整时,我们必须取消当前的 OpenGL 渲染上下文并从头开始重新创建它。
    LRESULT CMyControl::OnSize(UINT /*uMsg*/, WPARAM /*wParam*/,
        LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        // when resized, recreate the device context
        wglMakeCurrent(NULL,    NULL);
        if (m_hRC)
        {
            wglDeleteContext(m_hRC);
            m_hRC = NULL;
        }
        HDC hdc = GetDC();
        RECT rc;
        GetClientRect(&rc);
        CreateContext(hdc, rc);
    
        return 0;
    }

    全部完成。 记住! 您可以使用 glaux 库(glaux32.lib,<glaux.h>)来实现等效于 GLUT 库的功能(尽管您可能不需要窗口功能)。 例如,您可以使用 auxDIBImageLoad 加载位图以用作纹理。 但是,我建议使用更高级的纹理方法,例如使用 PNG 文件作为具有 Alpha 通道的纹理。

  7. 渲染可以在 OnDraw 中完成。 这是一个例子
    HRESULT OnDraw(ATL_DRAWINFO& di)
    {
        HDC hdc = di.hdcDraw;
        RECT& rc = *(RECT*)di.prcBounds;
    
    
        wglMakeCurrent(hdc, m_hRC);
    
        glClearColor(1.0f, 0.0f, 0.0f, 10.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    
        glPushMatrix();
    
        // compute dimensions of a quarter of the control
        SIZE qtrSize = { 
            (rc.right - rc.left) / 2, 
            (rc.bottom - rc.top) / 2 };
    
        glBegin(GL_QUADS);
            glColor3ub(255, 0, 0);
            glVertex3s(-qtrSize.cx, -qtrSize.cy, 0);
    
            glColor3ub(0, 255, 0);
            glVertex3s(qtrSize.cx, -qtrSize.cy, 0);
    
            glColor3ub(0, 0, 255);
            glVertex3s(qtrSize.cx, qtrSize.cy, 0);
    
            glColor3ub(255, 255, 255);
            glVertex3s(-qtrSize.cx, qtrSize.cy, 0);
        glEnd();
    
        glPopMatrix();
    
        glFinish();
        SwapBuffers(wglGetCurrentDC());
    
        return S_OK;
    }

可能的改进

我认为可以创建一个混入类/抽象超类,以比每个控件类中的一堆函数/消息处理程序更简洁的方式将 OpenGL 功能添加到 ATL 控件。 为每个 ATL 控件编写所有这些代码(如果您的项目中有许多控件)很繁琐:如果您可以指定您的控件继承自某个 CControlOpenGL<YourControl> 类,那就更好了。 此功能以及更高级的 OpenGL 模式和选项可能会构成另一个教程的材料。

© . All rights reserved.