老外的教程果然还是非常赞的,强烈推荐原版,这篇博客简单介绍一下步骤:
1.新建MFC工程
2.添加Picture Control控件
3.添加一个OpenGL控件类
4.在MFC中使用OpenGL控件
获取博主编译的测试工程(使用VS2013 X64平台,你还需要另外配置OpenGL相关环境),运行效果如左图所示,这个Demo还可以接受鼠标的控制,对模型进行操作。如果你需要多个OpenGL窗口,再实例化这个类,可以很轻易地实现多个窗口的绘制。这里就不翻译老外网站上的教程了,在上面给出了框架搭建的链接,这里就直接介绍该OpenGL类的使用方法吧。
这里特别强调一下第二步,以防阅读不仔细的同学产生不必要的错误。修改Picture Control控件:visible 为 FALSE,控件ID位 IDC_OPENGL。
你可能会有疑问,为什么visible要设置为FALSE?这里的控件只是提供了一个绘图区域的坐标信息,绘图过程是由Opengl根据坐标直接绘制的,与控件无关,如果控件存在,会遮挡住绘图区域,所以要设置visible位FALSE。
添加OpenGL控件,这个控件类的搭建方法在这里。我在构建代码时使用的OpenGL库为glew和freeglut库,如果你刚刚接触OpenGL,本人强力推荐NeHe的OpenGL教程。这里给出这个控件类的源码:
#ifndef OPENGL_CONTROL_ #define OPENGL_CONTROL_ #include "afxwin.h" #include <GL\glew.h> #include <GL\freeglut.h> #pragma comment(lib, "glew32s.lib") #ifdef _DEBUG #pragma comment(lib, "debug/freeglut_static.lib") #else #pragma comment(lib, "release/freeglut_static.lib") #endif class COpenGLControl : public CWnd { public: COpenGLControl(); ~COpenGLControl(); void oglCreate(CRect rect, CWnd *parent); void oglInitialize(void); void oglDrawScene(void); public: UINT_PTR m_unpTimer; float m_fLastX; float m_fLastY; bool m_bIsMaximized; GLfloat m_fPosX; // X position of model in camera view GLfloat m_fPosY; // Y position of model in camera view GLfloat m_fZoom; // Zoom on model in camera view GLfloat m_fRotX; // Rotation on model in camera view GLfloat m_fRotY; // Rotation on model in camera view public: CWnd* hWnd; HDC hdc; HGLRC hrc; int m_nPixelFormat; CRect m_rect; CRect m_oldWindow; CRect m_originalRect; afx_msg void OnPaint(); afx_msg void OnDraw(CDC *pDC); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnMouseMove(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() }; #endif
上面是类的头,下面是类的实现。
#include "stdafx.h" #include "OpenGLControl.h" COpenGLControl::COpenGLControl() { m_fPosX = 0.0f; // X position of model in camera view m_fPosY = 0.0f; // Y position of model in camera view m_fZoom = 10.0f; // Zoom on model in camera view m_fRotX = 0.0f; // Rotation on model in camera view m_fRotY = 0.0f; // Rotation on model in camera view m_bIsMaximized = false; } COpenGLControl::~COpenGLControl() { } BEGIN_MESSAGE_MAP(COpenGLControl, CWnd) ON_WM_PAINT() ON_WM_TIMER() ON_WM_SIZE() ON_WM_CREATE() ON_WM_MOUSEMOVE() END_MESSAGE_MAP() void COpenGLControl::oglCreate(CRect rect, CWnd *parent) { CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_OWNDC, NULL, (HBRUSH)GetStockObject(BLACK_BRUSH), NULL); CreateEx(0, className, L"OpenGL", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, rect, parent, 0); // Set initial variables' values m_oldWindow = rect; m_originalRect = rect; hWnd = parent; } void COpenGLControl::OnPaint() { //CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here // Do not call CWnd::OnPaint() for painting messages ValidateRect(NULL); CPaintDC dc(this); OnDraw(&dc); } void COpenGLControl::oglInitialize(void) { // Initial Setup: // static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32, // bit depth 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, // z-buffer depth 0, 0, 0, 0, 0, 0, 0, }; hdc = GetDC()->m_hDC; // Get device context only once. m_nPixelFormat = ChoosePixelFormat(hdc, &pfd); // Pixel format. SetPixelFormat(hdc, m_nPixelFormat, &pfd); hrc = wglCreateContext(hdc); // Create the OpenGL Rendering Context. wglMakeCurrent(hdc, hrc); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// Set color to use when clearing the background. glClearDepth(1.0f); glFrontFace(GL_CCW); // Turn on backface culling glCullFace(GL_BACK); glEnable(GL_DEPTH_TEST); // Turn on depth testing glDepthFunc(GL_LEQUAL); OnDraw(NULL); // Send draw request } void COpenGLControl::OnTimer(UINT_PTR nIDEvent) { // TODO: Add your message handler code here and/or call default switch (nIDEvent){ case 1:{ // Clear color and depth buffer bits glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw OpenGL scene oglDrawScene(); // Swap buffers SwapBuffers(hdc); break; } default: break; } CWnd::OnTimer(nIDEvent); } void COpenGLControl::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); // TODO: Add your message handler code here glViewport(0, 0, cx, cy); // Map the OpenGL coordinates. glMatrixMode(GL_PROJECTION); // Projection view glLoadIdentity(); gluPerspective(35.0f, (float)cx / (float)cy, 0.01f, 2000.0f); // Set our current view perspective glMatrixMode(GL_MODELVIEW); // Model view switch (nType){ // If window resize token is "maximize" case SIZE_MAXIMIZED:{ GetWindowRect(m_rect); // Get the current window rect MoveWindow(6, 6, cx - 14, cy - 14); // Move the window accordingly GetWindowRect(m_rect); // Get the new window rect m_oldWindow = m_rect; // Store our old window as the new rect break; } case SIZE_RESTORED:{ // If window resize token is "restore" if (m_bIsMaximized) {// If the window is currently maximized GetWindowRect(m_rect); // Get the current window rect MoveWindow(m_oldWindow.left, // Move the window accordingly (to our stored old window) m_oldWindow.top - 18, m_originalRect.Width() - 4, m_originalRect.Height() - 4); GetWindowRect(m_rect); // Get the new window rect m_oldWindow = m_rect; // Store our old window as the new rect } break; } } } void COpenGLControl::OnDraw(CDC *pDC) { // TODO: Camera controls. glLoadIdentity(); glTranslatef(0.0f, 0.0f, -m_fZoom); glTranslatef(m_fPosX, m_fPosY, 0.0f); glRotatef(m_fRotX, 1.0f, 0.0f, 0.0f); glRotatef(m_fRotY, 0.0f, 1.0f, 0.0f); } int COpenGLControl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here oglInitialize(); return 0; } void COpenGLControl::oglDrawScene(void) { // Wireframe Mode glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glBegin(GL_QUADS); { // Top Side glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Side glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Front Side glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); // Back Side glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(1.0f, -1.0f, -1.0f); // Left Side glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Right Side glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glVertex3f(1.0f, 1.0f, -1.0f); } glEnd(); } void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default int diffX = (int)(point.x - m_fLastX); int diffY = (int)(point.y - m_fLastY); m_fLastX = (float)point.x; m_fLastY = (float)point.y; // Left mouse button if (nFlags & MK_LBUTTON){ m_fRotX += (float)0.5f * diffY; if ((m_fRotX > 360.0f) || (m_fRotX < -360.0f)){ m_fRotX = 0.0f; } m_fRotY += (float)0.5f * diffX; if ((m_fRotY > 360.0f) || (m_fRotY < -360.0f)){ m_fRotY = 0.0f; } } // Right mouse button else if (nFlags & MK_RBUTTON){ m_fZoom -= (float)0.1f * diffY; } // Middle mouse button else if (nFlags & MK_MBUTTON){ m_fPosX += (float)0.05f * diffX; m_fPosY -= (float)0.05f * diffY; } OnDraw(NULL); CWnd::OnMouseMove(nFlags, point); }
下面进入正题,我们由教程中的方法构建了一个控件类,我们要在对话框类中使用该控件,需要在对话框类中修改如下代码:
在对话框类中包含该控件的头,并且添加控件变量
#include "OpenGLControl.h" class ...Dlg{.. ...... private: COpenGLControl m_oglWindow; ...... }
在对话框初始化时,添加如下代码:
CRect rect; // OpenGL Begin GetDlgItem(IDC_OPENGL)->GetWindowRect(rect); // Get size and position of the picture control ScreenToClient(rect); // Convert screen coordinates to client coordinates m_oglWindow.oglCreate(rect, this); // Create OpenGL Control window m_oglWindow.m_unpTimer = m_oglWindow.SetTimer(1, 1, 0); // Setup the OpenGL Window's timer to render
在类向导中添加WM_SIZE消息的处理:
void CMFCwithOpenglDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); // TODO: Add your message handler code here switch (nType){ case SIZE_RESTORED:{ if (m_oglWindow.m_bIsMaximized){ m_oglWindow.OnSize(nType, cx, cy); m_oglWindow.m_bIsMaximized = false; }break; } case SIZE_MAXIMIZED:{ m_oglWindow.OnSize(nType, cx, cy); m_oglWindow.m_bIsMaximized = true; break; } } }
这样我们就可以实现在MFC中绘制OpenGL了,如果有更多疑惑,请参照文章开头提供的源码,或者查看原版教程或对比源码(博主编译的版本和原版教程提供的源码均可编译通过),祝好。如果要使用OpenGL抓取图像,可以参照我下一篇博客。
OK, See You Next Chapter!