在 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。