WINDOWS下展示OpenGL有多种形式:
MFC 或 win32,该如何向MFC中添加OpenGL?下面是介绍最简单OpenGL框架。
1、首先通过VS建立MFC应用程序-MyOpenGL,选择单文档结构视图。
2、添加控制台窗体,帮助输出调试信息。
CMyOpenGLView.cpp添加头文件
#include <conio.h> #include <iostream> #include <fcntl.h> #include <io.h>
CMyOpenGLView::CMyOpenGLView()添加如下代码,使标准输入输出流和控制台建立连接。
其中MFC环境下cout和C语言的都可以使用,而WIN32下只能使用C语言的标准输出。
if ( AllocConsole() ) { int hCrt=_open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE),_O_TEXT); *stdout = *(::_fdopen(hCrt, "w")); ::setvbuf(stdout, NULL, _IONBF, 0); *stderr = *(::_fdopen(hCrt, "w")); ::setvbuf(stderr, NULL, _IONBF, 0); }
CMyOpenGLView::~CMyOpenGLView()中添加
FreeConsole()
2、规定设备显示时的像素格式,并建立OpenGL上下文,(什么?你不知道什么叫做WINDOWS设备和OpenGL上下文?出门右转 孙鑫VC++和OpenGL红宝书
通过MFC类向导添加CMyOpenGLView类的WM_CREATE消息,生成
int CMyOpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct),在里面添加
PIXELFORMATDESCRIPTOR pfd; int n; HGLRC hrc; pmDC=new CClientDC(this); if(!bSetupPixelFormat(pmDC)) return false; n=::GetPixelFormat(pmDC->GetSafeHdc()); ::DescribePixelFormat(pmDC->GetSafeHdc(), n,sizeof(pfd),&pfd); hrc=wglCreateContext(pmDC->GetSafeHdc()); wglMakeCurrent(pmDC->GetSafeHdc(),hrc);
其中
bSetupPixelFormat()的定义如下:
bool bSetupPixelFormat(CClientDC * pmDC) { static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL| PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 }; int pixelformat; if((pixelformat = ChoosePixelFormat(pmDC->GetSafeHdc(), &pfd)) == 0) { cout<<"ChoosePixelFormat failed"<<endl; return FALSE; } if(SetPixelFormat(pmDC->GetSafeHdc(), pixelformat, &pfd) == FALSE) { cout<<"SetPixelFormat failed"<<endl; return FALSE; } return TRUE; }
还要记得销毁,释放资源。
在OnDestroy中添加
void CMyOpenGLView::OnDestroy() { CView::OnDestroy(); // TODO: 在此处添加消息处理程序代码 HGLRC hrc; hrc = ::wglGetCurrentContext(); ::wglMakeCurrent(NULL,NULL); if(hrc) ::wglDeleteContext(hrc); if(pmDC) delete pmDC; }
这样 我们的上下文及设备就建立完毕,下面可以专注于OpenGL的事情了
3、OpenGL的初始化
在stdafx.h添加头文件
#include "glew.h" #include "glut.h"
并通过工程属性设置添加
glew32.lib glut.lib glut32.lib 这些就是OpenGL的函数库了
再次在OnCreate(..)函数中添加 代码初始化 glew和设置OpenGL的环境
GLenum err=glewInit(); if(GLEW_OK!=err) { return -1; } glEnable(GL_TEXTURE_2D); // Enable Texture Mapping ( NEW ) glShadeModel(GL_SMOOTH); // Enable Smooth Shading glClearColor(0.0f, 0.0f,0.0f,1.0f); // Black Background glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); // Enables Depth Testing glDepthFunc(GL_LESS); // The Type Of Depth Testing To Do //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glDisable(GL_BLEND); ::glDisable( GL_CULL_FACE ); // 开启表面剔除
然后在OnSize函数中添加代码建立投影矩阵和模型矩阵
// TODO: 在此处添加消息处理程序代码 int w=cx; int h=cy; if(h==0) h=1; //设置视口与窗口匹配 glViewport(0,0,w,h); //重新设置坐标系统 //投影矩阵 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)w/(GLfloat)h,10.0f,100000.0f); //模型矩阵 glMatrixMode(GL_MODELVIEW); glLoadIdentity();
4、完成DrawScreen的接口
我们希望绘制可以不断被调用,这里运用MFC的OnIdle()函数,(这个函数不懂?详见VC++)
在MyOpenGL.cpp里的CMyOpenGLApp类中用VS的MFC向导添加重载函数,然后在里面添加如下代码
BOOL CMyOpenGLApp::OnIdle(LONG lCount) { // TODO: 在此添加专用代码和/或调用基类 CWinApp::OnIdle(lCount); if(CMyOpenGLView::mOpenView) { CMyOpenGLView::mOpenView->DrawScene(); } return true; }
其中mOPenView是CMyOpenGLView的静态类公共成员
在类CMyOpenGLView中添加
static CMyOpenGLView * mOpenView;
并在MyOpenGLView.cpp的头部添加静态成员变量的初始化语句,详见C++静态成员变量初始化
CMyOpenGLView * CMyOpenGLView::mOpenView=0;
然后在 OnCreate中再次添加代码
mOpenView=this;
这样我们在其他类中就可以调用CMyOpenGLView类的自定义成员函数DrawScreen了
然后在CMyOpenGLApp类的OnIdle()中调用
5、书写DrawScreen()
void CMyOpenGLView::DrawScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glMatrixMode(GL_MODELVIEW); //glLoadIdentity(); glBegin(GL_TRIANGLES); glVertex3f(-10,10,-30); glColor3f(1.0,0.0,0.0); glVertex3f(10,10,-30); glColor3f(0.0,1.0,0.0); glVertex3f(0,0,-30); glColor3f(0.0,0.0,1.0); glEnd(); SwapBuffers(wglGetCurrentDC()); }
其意义详见 OpenGL NeHe教程,以后的绘制函数都可以写在这里了。
6、是不是大功告成了?但是拖拉窗体时,还会有错误,那是因为MFC默认会用白刷子刷新窗体,
而OpenGL的SwapBuffers已经帮我们刷新了,所以应该把MFC的禁制掉
怎么做呢?
6.1、重载CMyOpenGLView的消息响应函数WM_ERASEBKGND,改为如下形式
BOOL CMyOpenGLView::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 return true; }
6.2、在OnSize中也调用绘制函数,这样窗体在变形时也能绘制
DrawScene();
这样就成功了!