首先,使用的庫是GLUT以及GLAUX,先下載兩者,添加查找路徑以及鏈接
一、單文本文件
工程openGLMFC
1、創建單文本文件

2、添加路徑、鏈接
方法如之前篇章所示,
鏈接庫為opengl32.lib ;glu32.lib ;glut32.lib ;glaux.lib
3、頭文件
在stdafx.h中加入下列語句:
//openGL 所需要的頭文件 #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <gl/glaux.h>
4、設置窗口風格
將窗口風格設為
WS_CLIPCHILDREN和
WS_CLIPSIBLINGS,從而避免OpenGL繪制到其他窗口中去。這些應該放在PreCreateWindow()中。
BOOL CopenGLMFCView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; return CView::PreCreateWindow(cs); }
5、變量、函數聲明
初始化GL,考慮到使用設備上下文、繪圖上下文,
先在openGLMFCView.h中聲明需要的變量以及函數
HGLRC m_hRC; //RC 繪圖上下文 CDC* m_pDC; //DC 設備上下文 BOOL InitializeOpenGL(); //初始化 OpenGL BOOL SetupPixelFormat(); //為 DC 建立像素格式
6、創建消息響應函數
初始化需要在WM_CREATE觸發
資源釋放在WM_DESTROY觸發
窗口大小變化時
WM_SIZE觸發,需要調整繪圖
由於使用opengl繪制背景,故不需要窗口自己在繪制背景了,需要改寫
WM_ERASEBACKGROUND消息觸發的事件
故,打開classWizard,在openGLMFCView類中添加上面四個消息響應函數
OnCreate OnDestroy OnSize OnEraseBkground
7、初始化
將通過建立像素格式和繪制上下文來初始化OpenGL. 在InitializeOpenGL()中會創建一個設備上下文(DC),為這個DC選擇一個像素格式,創建和這個DC相關的繪制上下文(RC),然后選擇這個RC.這個函數會調用SetupPixelFormat()來建立像素格式。
int CopenGLMFCView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here InitializeOpenGL(); return 0; } BOOL CopenGLMFCView::InitializeOpenGL() { //客戶區獲得DC m_pDC = new CClientDC(this); //Failure to Get DC if (m_pDC == NULL) { MessageBox(L"Error Obtaining DC"); return FALSE; } //為DC建立像素格式 if (!SetupPixelFormat()) { return FALSE; } //創建 RC m_hRC = ::wglCreateContext(m_pDC->GetSafeHdc()); //Failure to Create Rendering Context if (m_hRC == 0) { MessageBox(L"Error Creating RC"); return FALSE; } //設定OpenGL當前線程的渲染環境。 //以后這個線程所有的OpenGL調用都是在這個hdc標識的設備上繪制。 if (::wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC) == FALSE) { MessageBox(L"Error making RC Current"); return FALSE; } //背景顏色 ::glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //深度緩存 1最大,讓任何都能顯示出來 ::glClearDepth(1.0f); //如果通過比較后深度值發生變化了,會進行更新深度緩沖區的操作 ::glEnable(GL_DEPTH_TEST); return TRUE; } //建立像素格式 ///////////////////////////////////////////////////////////////////////////// BOOL CopenGLMFCView::SetupPixelFormat() { static 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 16, // 16-bit z-buffer 0, // no stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; int m_nPixelFormat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd); if (m_nPixelFormat == 0) { return FALSE; } if (::SetPixelFormat(m_pDC->GetSafeHdc(), m_nPixelFormat, &pfd) == FALSE) { return FALSE; } return TRUE; }
8、繪制場景
在繪制場景時,一般包括如下步驟:1)清空緩存。2)繪制場景。3)Flush掉渲染流水線。4)若設置了雙緩沖,則交換前后台緩沖區。
void CopenGLMFCView::OnDraw(CDC* /*pDC*/) { CopenGLMFCDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: add draw code for native data here // 清除顏色、深度緩存 ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //可以添加渲染函數 // Flush掉渲染流水線 ::glFinish(); // 交換前后緩存區 ::SwapBuffers(m_pDC->GetSafeHdc()); }
9、背景繪制修改
試試改變窗口的大小,你會看到很嚴重的閃爍,並且關閉程序后會報告內存泄露,因此我們這就來解決這兩個問題吧。
發生閃爍的原因是Windows先繪制背景,然后再是OpenGL繪制,因為我們已經讓OpenGL負責清空背景色,因此我們不需要Windows去清空背景了
BOOL CopenGLMFCView::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default return TRUE;//CView::OnEraseBkgnd(pDC); }
內存泄露的原因是我們在InitializeOpenGL()中使用了new運算符來為CClientDC對象分配內存,因此需要顯示delete掉。
void CopenGLMFCView::OnDestroy() { CView::OnDestroy(); // TODO: Add your message handler code here if (::wglMakeCurrent(0, 0) == FALSE) { MessageBox(L"Could not make RC non-current"); } //Delete the rendering context if (::wglDeleteContext(m_hRC) == FALSE) { MessageBox(L"Could not delete RC"); } //Delete the DC if (m_pDC) { delete m_pDC; } //Set it to NULL m_pDC = NULL; }
10、大小調整
在OnSize()中一般用來設置視口和視錐,因為這些是和窗口大小相關的。基本操作包括設置視口,選擇投影矩陣,設置模型視圖矩陣。
void CopenGLMFCView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); // TODO: Add your message handler code here GLdouble aspect_ratio; // width/height ratio if (0 >= cx || 0 >= cy) { return; } // select the full client area ::glViewport(0, 0, cx, cy); // compute the aspect ratio // this will keep all dimension scales equal aspect_ratio = (GLdouble)cx / (GLdouble)cy; // select the projection matrix and clear it ::glMatrixMode(GL_PROJECTION); ::glLoadIdentity(); // select the viewing volume ::gluPerspective(45.0f, aspect_ratio, .01f, 200.0f); // switch back to the modelview matrix and clear it ::glMatrixMode(GL_MODELVIEW); ::glLoadIdentity(); }
通過上面得到運行結果:
二、基於對話框
工程:openGLMFCDialog
1-3步驟同上
4、變量函數聲明
在openGLMFCDialogDlg.h文件中,聲明如上。
其余步驟同上,不同點在於,需要將OnDraw里面的寫到
OnPaint函數里面,其中IsIconic()是判斷是否為最小化,可以放到else里面即可。
得到結果
