引言
我們知道,在MFC框架中,用於繪圖的接口是GDI。但GDI只能繪制簡單的2D圖形,要想制作精美的3D圖形,一個可行的辦法是使用OpenGL或者Direct3D等第三方庫。
由於最近在給導師的一個小項目幫忙,而且要求使用OpenGL,所以我特地在網上搜索“如何在MFC框架中使用OpenGL”,看了很多博文,甚至論文(居然還有人把這個寫成論文)后,自己又到VS2012上嘗試了一番,最終摸索出了最最簡單基本的使用方法,故總結在這里。一方面開啟自己在博客園的學習和自省之路,另一方面也為需要的朋友提供點小小的幫助。相信這個實例會是最簡單最容易理解的。
摘要
GDI繪圖使用的是HDC,而OpenGL使用的則是HGLRC。與D2D通過把RenderTarget綁定到HDC以實現和GDI的混用的方法類似,OpenGL要想與HDC混用,或者說兼容HDC吧,需要使用wglCreateContext()函數來通過HDC創建一個HGLRC並把它選為當前所使用的環境。不過在此之前,我們還需要把像素格式設置成支持OpenGL的格式。另外,窗口也必須被改為特定的樣式才能被OpenGL使用。做好了這三點,就能在MFC框架中使用OpenGL進行繪制了。
具體實現步驟
1. 封裝OpenGL類
首先,基於OOP的思想,我們把跟OpenGL相關的數據與操作封裝起來:
#pragma once class COpenGL { private: HDC hDC; //GDI繪圖中使用的的設備環境句柄 HGLRC hRC; //OpenGL渲染時使用的渲染環境句柄 public: COpenGL(void); virtual ~COpenGL(void); bool SetupPixelFormat(HDC hdc); //設置像素格式為適合OpenGL的格式 void Init(void); //初始化渲染過程中屬性的設置 void Render(void); //繪制代碼 void Reshape(int width,int height); //改變窗口大小時對視窗進行的操作 };
#include "stdafx.h" #include "OpenGL.h" COpenGL::COpenGL(void) { } COpenGL::~COpenGL(void) { wglMakeCurrent(hDC, NULL); wglDeleteContext(hRC); } void COpenGL::Init(void) { //可添加例如“開啟深度探測”等繪圖屬性設置代碼 } bool COpenGL::SetupPixelFormat(HDC hdc) { hDC=hdc; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // pfd結構的大小 1, // 版本號 PFD_DRAW_TO_WINDOW | // 支持在窗口中繪圖 PFD_SUPPORT_OPENGL | // 支持OpenGL PFD_DOUBLEBUFFER, // 雙緩存模式 PFD_TYPE_RGBA, // RGBA 顏色模式 24, // 24 位顏色深度 0, 0, 0, 0, 0, 0, // 忽略顏色位 0, // 沒有非透明度緩存 0, // 忽略移位位 0, // 無累加緩存 0, 0, 0, 0, // 忽略累加位 32, // 32 位深度緩存 0, // 無模板緩存 0, // 無輔助緩存 PFD_MAIN_PLANE, // 主層 0, // 保留 0, 0, 0 // 忽略層,可見性和損毀掩模 }; int nPixelFormat; // 像素點格式 if (!(nPixelFormat = ChoosePixelFormat(hDC, &pfd))) { MessageBox(NULL,L"can not find proper mode",L"Error",MB_OK|MB_ICONEXCLAMATION); return FALSE; } SetPixelFormat(hDC,nPixelFormat,&pfd); hRC = wglCreateContext(hDC); //利用GDI繪圖所使用的HDC創建對應的HGLRC wglMakeCurrent(hDC, hRC); //使OpenGL繪圖所使用的HGLRC為當前繪圖工具 return TRUE; } void COpenGL::Render() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); //繪制操作: glColor3ub(255,0,0); glBegin(GL_POLYGON);//填充凸多邊形 glVertex3f(0.5f,0.5f ,0.0f); glVertex3f(0.5f,-0.5f, 0.0f); glVertex3f(-0.5f,-0.5f,0.0f); glVertex3f(-0.5f,0.5f,0.0f); glEnd(); glFlush(); SwapBuffers(hDC); } void COpenGL::Reshape(int width,int height) { glViewport(0,0,width,height); }
2. 使用OpenGL類
接下來,我們在MFC框架中的CProjNameView類中創建剛剛編寫的OpenGL類成員並進行相應的調用和操作來進行繪制:
先在頭文件中添加成員變量:
COpenGL opengl;
然后,改寫CProjNameView::PreCreateWindow()函數以改變窗口樣式來適應OpenGL的要求:
BOOL CProjNameView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: 在此處通過修改 // CREATESTRUCT cs 來修改窗口類或樣式 cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN; return CView::PreCreateWindow(cs); }
再在CProjNameView::OnInitialUpdate()函數中添加設置像素格式、轉換當前繪圖所使用的環境和初始化OpenGL繪制屬性的操作。(這里我們省去了初始化操作,即省去了OpenGL.cpp中的Init()函數的代碼。設置像素格式、轉換當前繪圖所使用的環境都包含在COpenGL類的成員函數SetupPixelFormat()中)
void CProjNameView::OnInitialUpdate() { CView::OnInitialUpdate(); // TODO: 寫入最終選擇模式代碼之后移除此代碼 m_pSelection = NULL; // 初始化所選內容 opengl.SetupPixelFormat(::GetDC(GetSafeHwnd())); opengl.Init(); }
接下來,在CProjNameView::OnDraw()函數中添加繪制操作。
void CProjNameView::OnDraw(CDC* /*pDC*/) { CFaceModelingDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: 在此處為本機數據添加繪制代碼 opengl.Render(); }
最后,再改寫CProjNameView::OnSize()函數,就大功告成了!(當然,這個並不是本例子中必要的,沒有這項控制也能成功繪制。)
void CProjNameView::OnSize(UINT nType, int cx, int cy) { if(cx==0) { cx=1; } opengl.Reshape(cx,cy); }
效果圖如下:

補充
當然,事先需要包含gl.h和glut.h兩個頭文件,他們一般都在GL文件夾目錄下,而且gl.h是自帶的,glut.h是需要自己擴展的。另外,還需要把glut.lib添加到工程可見的范圍內。方便起見,我在這里提供一下glut的下載鏈接:glut擴展包
另外,在MFC中使用OpenGL的繪制列表和紋理時要注意,MFC貌似不支持OpenGL的繪制列表,而且MFC似乎要求在每次繪制前都要設置一次紋理。這之中的道理只有深入了解MFC和OpenGL后才能明白了,如果有朋友知道的話,希望能在評論里給一些指導哦~!
如果還有什么問題或是建議的話,希望大家提出來!新人第一次寫隨筆,還請多多指教~!
