1. 程式人生 > >MFC下二維OpenGL環境詳細配置

MFC下二維OpenGL環境詳細配置

一直以來,網上有很多關於OpenGL在MFC環境下配置的教程,但是,一般都說的不夠詳細,或者配置過程不夠完整,今天我在自己摸索和學習的基礎了,寫出了這篇文章,儘量說明了座標系的設定、新增深度測試型別以防止顏色變淡或者不純等等。首先說明,本配置教程配置完成的OpenGL環境為二維環境,座標系為中心座標系,客戶區中心為OpenGL座標系的中心,向右為x軸增大方向;向上為y軸增大方向,與其他的配置方法不太一樣,如果想對座標系進行修改,只需修改ReSizeGLScene函式中的下面一句:

//引數分別代表(左下角x座標,右上角x座標,左下角y座標,右上角y座標)——座標全相對於視窗左下角--原點
	gluOrtho2D(-cx/2, cx/2, -cy/2, cy/2);   

(1)     新建一個MFC單文件應用程式,程式建立完成後,會自動設定一個視類,如CdispView類,OpenGL是圖形操作,所以所有的操作都是在視類上進行的。

(2)     新增OpenGL標頭檔案和庫檔案,1510實驗室使用的是基於OpenGL4.0標準的最新版freeglut3.0開源庫,OpenGL開源庫種類很多,比較出名的有最早的glut庫、OpenLuGL等等,這裡選中這個庫的原因是freeglut原生支援32/64位系統,並且有針對64位系統優化的程式碼,方便後期升級,此外,這個庫在SDK上基本保持了與glut的一致性,方便學習。下面(3)就來詳細的講如何配置

(3)     將FreeGlut_OpenGL_Dll資料夾中標頭檔案資料夾開啟,將其中的四個標頭檔案新增到工程的附加包含目錄下,或直接將其放在工程對應的目錄下;將FreeGlut_OpenGL_Dll資料夾中32位庫資料夾開啟(64位系統也可以用32位庫,目前開發的時候也是按32位庫進行開發的,相容性更好一點),將freeglut.lib和freeglut.dll兩個dll檔案新增到工程的附加庫目錄下,或直接將其放在工程對應的目錄下;

(4)     在視類的標頭檔案中包含OpenGL庫:

//新增OpenGL標頭檔案,呼叫OpenGL庫

#include"glut.h"

如果還需要用牛四強寫的OpenGL的常用畫圖函式,需要新增下面程式碼和相應的標頭檔案、庫檔案

//呼叫我自己寫的OpenGL常用畫圖函式

#include"OpenGLCommonDrawHead.h"

#pragmacomment(lib,"OpenGLCommonDraw.lib")

(5)     在類的屬性欄,為下述訊息加入訊息處理函式:WM_CREATE (for OnCreate), WM_DESTROY (for OnDestroy), WM_SIZE (forOnSize), WM_ERASEBACKGROUND (for OnEraseBkground),如下圖所示:

(6)     設定視窗顯示風格。視窗建立之前我們必須設定視窗風格包含WS_CLIPCHILDREN和WS_CLIPSIBLINGS,從而避免OpenGL繪製到其他視窗中去。這些應該放在PreCreateWindow()中。

程式碼如下:

BOOL CfirstView::PreCreateWindow(CREATESTRUCT& cs)

{
// TODO: 在此處通過修改
// CREATESTRUCT cs 來修改視窗類或樣式
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;//Tramp
return CView::PreCreateWindow(cs);
}

(7)     在視類的標頭檔案中新增private型別的變數

 /************************************************************************/

    /* 設定的變數是Rendering Context(著色描述表)。每一個OpenGL都被連線到一個著

    色描述表上。著色描述表將所有的OpenGL呼叫命令連線到Device Context(裝置描述表)上。

    我將OpenGL的著色描述表定義為hRC 。要讓您的程式能夠繪製視窗的話,還需要建立一個

    裝置描述表,也就是第二行的內容。Windows的裝置描述表被定義為hDC 。DC將視窗連線到

    GDI(Graphics Device Interface圖形裝置介面)。而RC將OpenGL連線到DC                                                                     */

    /************************************************************************/

	//-----------------------------------------
	//-----------------------------------------
	//           OpenGL及MFC配置引數
	//-----------------------------------------
	//-----------------------------------------
    HGLRC m_hRC;    //Rendering Context著色描述表
    CDC* m_pDC;        //Device Context裝置描述表
	//客戶區大小,分別代表x軸和y軸大小,也就是寬和高
	int m_cxClient;
	int m_cyClient;

(8)     在視類的標頭檔案中定義相應的public初始化函式、縮放函式,並在CPP中填寫函式內容:

標頭檔案宣告:

BOOL InitializeOpenGL();    //初始化OpenGL函式
	BOOL SetupPixelFormat();    //設定畫素格式函式
	//視窗大小變化時調整的自動縮放函式
	GLvoid ReSizeGLScene(int cx,int cy);  
	//畫圖函式
	void RenderScene();  
CPP檔案程式碼具體內容:
BOOL CDispWaveShowView::InitializeOpenGL()
{
	//獲取客戶區DC
	m_pDC = new CClientDC(this);
	//判斷獲取是否成功
	if(m_pDC == NULL)
	{
		MessageBox(_T("初始化OpenGL時無法獲取當前DC"));
		return FALSE;
	}

	//設定畫素格式
	if(!SetupPixelFormat())
	{
		MessageBox(_T("初始化OpenGL時無法設定畫素格式"));
		return FALSE;
	}

	//建立著色描述表
	m_hRC = ::wglCreateContext (m_pDC->GetSafeHdc ());
	//判斷是否成功
	if(m_hRC == 0)
	{
		MessageBox(_T("初始化OpenGL時建立RC失敗"));
		return FALSE;
	}

	//設定當前RC

	if(::wglMakeCurrent (m_pDC->GetSafeHdc (), m_hRC)==FALSE)
	{
		MessageBox(_T("初始化OpenGL時設定當前RC失敗"));
		return FALSE;
	}

	//OpenGL啟用雙緩衝
	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);

	//設定黑色背景並清空
	//::glClearColor(0.0f,0.0f,0.0f,0.0f);

	//設定緩衝並清空
	::glClearDepth(1.0f);

	//啟用深度測試 
	::glEnable(GL_DEPTH_TEST);

	glDepthFunc(GL_LEQUAL);		// 所作深度測試的型別
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// 告訴系統對透視進行修正


	glShadeModel(GL_SMOOTH);						// 啟用陰影平滑



	return TRUE;

}

BOOL CDispWaveShowView::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
		32,                             // 32-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 )
	{
		MessageBox(_T("選擇畫素格式失敗!"));
		return FALSE;
	}

	//設定畫素格式 
	if ( ::SetPixelFormat(m_pDC->GetSafeHdc(), m_nPixelFormat, &pfd) == FALSE)
	{
		MessageBox(_T("設定畫素格式失敗!"));
		return FALSE;
	}

	return TRUE;
}

(9)     在OnCreate中我們將通過建立畫素格式和繪製上下文來初始化OpenGL. 在InitializeOpenGL()中會建立一個裝置上下文(DC),為這個DC選擇一個畫素格式,建立和這個DC相關的繪製上下文(RC),然後選擇這個RC.這個函式會呼叫SetupPixelFormat()來建立畫素格式,程式碼如下:

	InitializeOpenGL();//初始化openGL環境
	glEnable(GL_BLEND);             //啟用混合功能,將圖形顏色同周圍顏色相混合  
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);   
	//OpenGL效果設定
	glEnable(GL_POLYGON_SMOOTH);     //多邊形抗鋸齒  
	glHint(GL_POLYGON_SMOOTH,GL_NICEST);
	glEnable(GL_LINE_SMOOTH);        //線抗鋸齒  
	glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
	glEnable(GL_POINT_SMOOTH);       //點抗鋸齒  
	glHint(GL_POINT_SMOOTH,GL_NICEST);

(10)     在OnSize()中一般用來設定視口和視錐,因為這些是和視窗大小相關的。基本操作包括設定視口,選擇投影矩陣,設定模型檢視矩陣。

//獲取視窗大小
	m_cxClient = cx;
	m_cyClient = cy;
	//根據視窗大小縮放
	ReSizeGLScene( cx, cy );
其中ReSizeGLScene的內容如下:
GLvoid CDispWaveShowView::ReSizeGLScene(int cx,int cy)
{
	//設定視口大小為整個客戶區大小 
	glViewport(0, 0, cx, cy);
	//計算寬度/高度比
	// this will keep all dimension scales equal
	//W_H_Ratio = (GLdouble)cx/(GLdouble)cy;
	//  設定變換模式為投影變換   
	glMatrixMode(GL_PROJECTION);
	// 初始化模型變換矩陣  
	::glLoadIdentity();
	// select the viewing volume
	//引數分別代表(左下角x座標,右上角x座標,左下角y座標,右上角y座標)——座標全相對於視窗左下角--原點
	gluOrtho2D(-cx/2, cx/2, -cy/2, cy/2);    
	//設定變換模式為模型變換 
	glMatrixMode(GL_MODELVIEW);
	// 初始化模型變換矩陣  
	::glLoadIdentity();
}

(11)     在繪製場景時,一般包括如下步驟:1)清空快取。2)繪製場景。3)Flush掉渲染流水線。4)若設定了雙緩衝,則交換前後臺緩衝區。

在視類的OnDraw函式裡面呼叫RenderScene()畫圖函式,畫圖函式的大框架如下:

void CDispWaveShowView::RenderScene()
{
	

	//清空背景和緩衝
	::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


	//進行換圖的操作
	//操作啊~我畫圖啊~~


	
	// 告訴OpenGL我已經畫完了
	::glFinish();


	//釋放緩衝區
	::SwapBuffers( m_pDC->GetSafeHdc() );
}

(12)     為了使改變視窗大小時嚴重的閃爍,在OnEraseBkgnd裡做一些操作,避免windows自己的視窗重新整理閃爍。把原來的return一堆直接改成return TRUE。

BOOL CDispWaveShowView::OnEraseBkgnd(CDC* pDC)
{
	return TRUE;
}

(13)     為了避免記憶體洩露,我們要將在SetupPixelFormat()中使用了new運算子來為CClientDC物件分配的記憶體在程式關閉時delete掉,即OnDestroy事件。

	//取消RC的佔用
	if(::wglMakeCurrent (0,0) == FALSE)
	{
		MessageBox(_T("Could not make RC non-current"));
	}
	//刪除RC
	if(::wglDeleteContext (m_hRC)==FALSE)
	{
		MessageBox(_T("Could not delete RC"));
	}
	//刪除DC
	if(m_pDC)
	{
		delete m_pDC;
	}

	//DC置為NULL
	m_pDC = NULL;