老外的教程果然还是非常赞的,强烈推荐原版,这篇博客简单介绍一下步骤:
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!