在 Windows 窗体上创建 OpenGL 视图






4.72/5 (41投票s)
2006 年 10 月 20 日
3分钟阅读

686688

20981
如何在 Windows 窗体上创建 OpenGL 视图。

引言
本文介绍了一种非常简洁明了的方法,使用托管 C++ 在 Windows 窗体上渲染 OpenGL 窗口。此方法不需要任何插件、第三方 DLL 或 ActiveX 组件,而这些组件通常通过 C# Windows 窗体演示。此实现是纯 .NET,使用托管 C++。本文假定读者具有 OpenGL 的工作知识,并且正在使用 Microsoft Visual Studio .NET 8.0。
背景
在 MFC 视图上呈现 OpenGL 窗口非常简单易于实现。然而,Windows 窗体的出现带来了一些问题。窗口设备上下文的概念在 Windows 窗体中并不真正存在。已经有一些在 Windows 窗体上实现 OpenGL 的方法,但这些方法依赖于包含 MFC 或 Win32 SDK 调用的 DLL 或 ActiveX 组件。这些实现也假设程序员正在使用 C#。虽然使用 C# 有一些优势,但我怀疑,像我自己一样,我不想为了利用 Windows 窗体而用 C# 重写我现有的应用程序。我也怀疑许多 C++ 程序员不希望或不能因为公司原因或其他原因而迁移到 C#。这里的实现使用纯托管 C++ 和 .NET。
实现
首先,我们需要一个新的 Windows 窗体应用程序来工作。创建一个新的 Windows 窗体应用程序(文件 -> 新建 -> 项目 -> Visual C++ -> CLR -> Windows 窗体应用程序)。
现在,我们将构建一个可以在多个 Windows 窗体上使用的 OpenGL 类。这可以很容易地转换为可以添加到设计时设计器的控件,但我不会介绍这一点。
为该类创建一个新的头文件,我将其命名为OpenGL.h,并将以下代码段复制到其中。我将在代码片段下方注释代码的实际作用: -
#pragma once #include <windows.h> #include <GL/gl.h> using namespace System::Windows::Forms; namespace OpenGLForm { public ref class COpenGL: public System::Windows::Forms::NativeWindow
我们在这里所做的只是包含 OpenGL 所需的头文件,并为 OpenGL 声明一个托管 C++ 类。
{
public:
    COpenGL(System::Windows::Forms::Form ^ parentForm, 
            GLsizei iWidth, GLsizei iHeight)
    {
        CreateParams^ cp = gcnew CreateParams;
        // Set the position on the form
        cp->X = 100;
        cp->Y = 100;
        cp->Height = iWidth;
        cp->Width = iHeight;
        // Specify the form as the parent.
        cp->Parent = parentForm->Handle;
        // Create as a child of the specified parent
        // and make OpenGL compliant (no clipping)
        cp->Style = WS_CHILD | WS_VISIBLE | 
                    WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
        // Create the actual window
        this->CreateHandle(cp);
        m_hDC = GetDC((HWND)this->Handle.ToPointer());
        if(m_hDC)
            MySetPixelFormat(m_hDC);
    }
此构造函数接受父窗体、OpenGL 区域的宽度和 OpenGL 区域的高度作为参数。
System::Windows::Forms::CreateParams 结构允许我们创建一个 Win32 窗口。我们指定 WS_CLIPSIBLINGS | WS_CLIPCHILDREN 以便我们的 OpenGL 显示不会被剪裁。
virtual System::Void Render(System::Void) { // Clear the color and depth buffers. glClearColor(0.0f, 0.0f, 0.0f, 0.0f) ; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
这是一个重写的函数,它只是用黑色填充 OpenGL 区域。
System::Void SwapOpenGLBuffers(System::Void)
{
    SwapBuffers(m_hDC) ;
}
在 OpenGL 渲染之后直接调用此函数,以将 OpenGL 缓冲区的内容放入我们的窗口中。
private:
    HDC m_hDC;
    HGLRC m_hglrc;
这些是我们与窗体交互所需的指针。
protected: ~COpenGL(System::Void) { this->DestroyHandle(); }
销毁窗体时,我们处理我们的窗口。
    GLint MySetPixelFormat(HDC hdc)
    {
        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 
        }; 
    
        GLint  iPixelFormat; 
     
        // get the device context's best, available pixel format match 
        if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
        {
            MessageBox::Show("ChoosePixelFormat Failed");
            return 0;
        }
         
        // make that match the device context's current pixel format 
        if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
        {
            MessageBox::Show("SetPixelFormat Failed");
            return 0;
        }
    
        if((m_hglrc = wglCreateContext(m_hDC)) == NULL)
        {
            MessageBox::Show("wglCreateContext Failed");
            return 0;
        }
        
        if((wglMakeCurrent(m_hDC, m_hglrc)) == NULL)
        {
            MessageBox::Show("wglMakeCurrent Failed");
            return 0;
        }
    
        return 1;
    }
};
用于为 Win32 SDK / MFC 设备上下文设置像素格式的标准函数。有关此内容的更多信息,请参见 OpenGL 网站。
如果您复制了所有代码段,它应该可以正常构建,请不要忘记将openGL32.lib、gdi32.lib和User32.lib添加到您的项目(项目 -> 属性 -> 配置 -> 所有配置 -> 链接器 -> 输入)。
测试容器
打开您的窗体的代码视图,并将头文件添加到您的 OpenGL 类,并在您的窗体中添加一个指向该类的成员变量。接下来,更改您的窗体的构造函数: -
OpenGL = gcnew COpenGL(this, 640, 480);
如果您现在重写您的Paint()函数(窗体设计器 -> 属性 -> 事件 -> 外观 -> Paint)并从此函数调用您的 OpenGL 渲染器
private: System::Void timer1_Tick(System::Object^  sender, 
                                  System::EventArgs^  e)
{
    UNREFERENCED_PARAMETER(sender);
    UNREFERENCED_PARAMETER(e);
    OpenGL->Render();
    OpenGL->SwapOpenGLBuffers();
}
运行您的应用程序!就是这样,简单!在 Windows 窗体上使用 OpenGL!
关注点
此方法有效,因为 System::Windows::Forms::NativeWindow 提供了窗口句柄和窗口过程的低级封装。这允许我们将该类视为 HWND(请参阅我的用户控件)。
我稍微更改了演示,以显示它更可能被使用。我这样做是因为我特意保持 OpenGL 的简单性(我甚至没有设置视口或视锥,但这是完全特定于应用程序的,我的文章旨在展示在 Windows 窗体上创建 OpenGL 视图的方法)。
历史
- 2006/10/18 - 将文章提交到 CodeProject。
