馬馬虎虎的看完了《3D數學基礎:圖形與游戲開發》和《OpenGL編程指南》,感覺沒看明白的跟看明白的一樣多...但還是算入門了吧!辭職以后,在家玩的甚歡,太過得以忘形了。這兩天收斂了許多,做了一個簡單的程序,這里想做一些相應的總結。
文章根據網絡上同名《Visual C++下OpenGL開發框架與應用》改寫,主要是問了節省時間,并且總結一下OpenGL在MFC中的多文檔框架下的搭建過程,加以備忘。下面是這兩天做的程序的截圖,特此留念!

以下是《Visual C++下OpenGL開發框架與應用》內容:
OpenGL全稱"開放式圖形庫",是由SGI公司開發的低層三維圖形API,目前在圖形開發領域已經成為工業標準。現今市面上關于OpenGL方面的書籍不在少數,但是大多是講解句法和實例,缺乏對其整個程序開發框架的總結與把握,所以總體上顯得比較凌亂。本篇文章主要針對初學者(最好要有圖形方面的基礎知識)而制作的,旨在通過對OpenGL 的整個知識結構的介紹,來具體剖析其內在運行機制,并且結合實際開發經驗總結出在VC平臺下的OpenGL開發框架,最后給出一個例程來說明這一框架的具體應用。
一、OpenGL基礎知識
OpenGL是一種開放式的圖形軟件開發包,它采用C語言風格,提供大量的函數來進行圖形方面的處理,一般編程使用的函數庫包括:
OpenGL圖形庫-----函數以gl開頭,可以實現比較簡單的繪制功能,核心函數共115個。這些函數可以運行在現在任何主流操作系統中。
OpenGL實用庫-----函數以glu開頭,其函數功能更高級一些,如繪制復雜的曲線曲面、高級坐標變換、多邊形分割等,共有43個。這些函數可以運行在現在任何主流操作系統中。
OpenGL輔助庫-----函數以aux開頭,它們是一些特殊的函數,包括簡單的窗口管理、輸入事件處理、某些復雜三維物體繪制等函數,共有31個。它只能在Win32平臺下運行。
OpenGL實用工具開發庫----函數以glut開頭,它們提供更為復雜的繪制功能,此函數由glut.dll來負責解釋執行。
Windows專用函數庫-----以wgl開頭,負責OpenGL與Windows窗口系統的連接,共有6個。
Win32函數------無專用前綴,實際上為API函數,共5個,用來處理比如象素格式的選擇及雙緩沖等功能。
OpenGL提供的函數一般是以客戶機/服務器的模式來運行的,即執行繪制圖形功能的應用程序作為客戶機,而OpenGL函數庫(實際上是一些動態鏈接庫,比如opengl32.dll,glu.dll等)作為服務器,當應用程序發出繪制請求時,服務器負責對這些繪制請求進行解釋,然后把這些處理過的請求發送給圖形顯示硬件,這樣就實現了繪圖的目的。另外由于它這種特有的運行機制也實現了網絡的透明性,即當應用程序與核心圖形庫不在同一臺機器上時,其程序的代碼完全跟它們在同一臺機器上的一樣,節約了通訊開銷。
那么在Windows操作平臺下,如果使用OpenGL圖形庫函數來開發應用程序呢?我們知道,使用GDI(圖形設備接口)開發應用程序時,首先需要獲得一個Device Context(設備描述表,簡稱DC),然后才能在這個DC下完成繪圖工作,這一過程就類似于現實生活中紙和筆的關系,DC就是紙,而象刷子、畫筆這樣的GDI對象就是筆。從Windows內部運行機制來分析,DC應該理解為狀態保持器,就是它可以而且必須保存當前系統的狀態,這些狀態包括:當前的畫筆、刷子等GDI的具體類型(顏色、粗細等),當前的調色板類型以及系統的其他信息。當用戶開始在DC上進行繪制工作時,系統就會先查看DC中相應的當前狀態值,然后利用這些狀態值進行圖形繪制,如果用戶希望改變當前狀態值,那么可以通過SelectObject這樣的Win32函數來將指定的狀態或者對象選入DC即可。
實際上基于OpenGL的應用程序也是這樣的,只是這里不是直接在DC上進行繪制工作,而是通過Render Context(渲染描述表或者繪制描述表,簡稱為RC)這樣一個橋梁在DC上進行繪制工作,對于程序來說實際上可以理解為就是在RC上繪制圖形。另外只要RC不被釋放(有效),那么就可以進行繪制工作;相比之下,DC卻需要不斷的創建和釋放。
所以要使用OpenGL圖形庫進行圖形應用開發,首先要獲得RC,然后要將其設置為"當前RC",最后后面所有的繪制工作都是在"當前RC"下面進行的,直到"當前RC"無效為止。
二、VC下的OpenGL的程序框架
在目前眾多的Windows應用程序開發工具中,微軟公司的VC6.0已經成為OpenGL圖形應用的首選開發工具。而要使用OpenGL圖形庫來開發2D/3D的應用程序,就必須解決程序框架的問題。由前面介紹的基礎知識可以清楚的看到,不能直接象利用GDI開發圖形程序那樣使用OpenGL,以下就介紹多文檔應用程序情況下的開發框架,其步驟為:
1. 首先在視圖類的PreCreateWindow函數內設置窗口類型,防止在窗口重疊時把圖形繪制到子窗口和兄弟窗口。實現代碼如下:
cs.style |=WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
2. 然后在視圖類的OnCreate函數下面進行OpenGL的初始化工作,這部分是此框架里最主要的代碼,具體包括:獲取視圖設備描述表、設置合適的象素格式和調色板、創建繪制描述表并將其設置為當前RC。其實現代碼見后面的例程(這部分的代碼基本上所有的程序都一致)。這一步完成后即可進行圖形的繪制工作。
3. 在視圖類的OnSize函數下面進行視口變換,代碼見后面例程。
4. 如果需要定時器的數據驅動,那么可以在視圖類的OnTimer下修改數據并調用OnDraw函數即可。
5. 在OnDestroy函數執行RC及DC的銷毀工作,釋放資源。詳見后面的代碼。
由上面的框架可以看出,所有的關于OpenGL的程序操作都是在指定的視圖類中完成的,核心就是OnCreate內的函數代碼,而這部分的代碼在大部分程序里面是雷同的,所以后面例程的代碼具有很大的通用性。另外,雖然上面只是討論了多文檔的情況,實際上單文檔的開發框架與其完全類似,這里就不多贅述了。
三、例程分析
3D游戲里面經常會出現地形這一三維實體,下面的例程就是在一個單文檔應用程序下實現3D地形的顯示。大概思路是建立OpenGL的程序框架,然后初始化地形數據,在此框架上繪制3D地形,由于為了介紹框架,所以程序中涉及到地形數據的初始化和繪制部分都比較簡單,詳見本文的源代碼部分。另外這里也沒有涉及到紋理映射、材質等高級內容,但是實際編程中,為了使3D圖形更生動,往往要應用這些技術。
此例程的核心代碼及分析如下:
BOOL CTrafficView::PreCreateWindow(CREATESTRUCT& cs)


{
// TODO: 在此處通過修改 CREATESTRUCT cs 來修改窗口類或
// 樣式
// OpenGL的要求:設置窗口類型
cs.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
// MDI 應用的要求:
// cs.lpszClass = AfxRegisterWndClass(CS_OWNDC | CS_HREDRAW | CS_VREDRAW);

return CView::PreCreateWindow(cs);
}
void CTrafficView::OnPaint()


{
CPaintDC dc(this); // device context for painting
// TODO: 在此處添加消息處理程序代碼
// 不為繪圖消息調用 CView::OnPaint()
RenderScene();
}
int CTrafficView::OnCreate(LPCREATESTRUCT lpCreateStruct)


{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

// TODO: 在此添加您專用的創建代碼
//獲取客戶區的設備描述表
m_pDC=new CClientDC(this);
//初始化OpenGL
InitializeOpenGL(m_pDC);
//初始化OpenGL的一些狀態參數并對地形數據進行初始化
InitGL();
return 0;
}

BOOL CTrafficView::InitializeOpenGL(CDC *pDC)


{
//進行opengl的初始化工作
m_pDC=pDC;
//首先把DC的象素格式調整為指定的格式,以便后面對DC的使用
SetupPixelFormat();
//根據DC來創建RC
m_hRC=::wglCreateContext(m_pDC->GetSafeHdc());
//設置當前的RC,以后的畫圖操作都畫在m_pDC指向的DC上
::wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC);
//下面可以進行畫圖操作了
return TRUE;
}
BOOL CTrafficView::SetupPixelFormat()


{
//初始化象素格式以及選取合適的格式來創建RC

PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // pfd結構的大小
1, // 版本號
PFD_DRAW_TO_WINDOW | // 支持在窗口中繪圖
PFD_SUPPORT_OPENGL | // 支持 OpenGL
PFD_DOUBLEBUFFER, // 雙緩存模式
PFD_TYPE_RGBA, // RGBA 顏色模式
24, // 24 位顏色深度 ,color depth
0, 0, 0, 0, 0, 0, // 忽略顏色位
0, // 沒有非透明度緩存
0, // 忽略移位位
0, // 無累加緩存
0, 0, 0, 0, // 忽略累加位
32, // 32 位深度緩存
0, // 無模板緩存
0, // 無輔助緩存
PFD_MAIN_PLANE, // 主層
0, // 保留
0, 0, 0 // 忽略層,可見性和損毀掩模
};
//在DC中選擇合適的象素格式并返回索引號
int pixelformat;
pixelformat=::ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);
if (pixelformat==0)

{
AfxMessageBox("no matched pixelformat!");
return FALSE;
}
//設置指定象素格式
if (::SetPixelFormat(m_pDC->GetSafeHdc(),pixelformat,&pfd)==FALSE)

{
AfxMessageBox("can't set specified pixelformat!");
return FALSE;
}
return TRUE;
}
BOOL CTrafficView::InitGL()


{
//初始化整個場景和OpenGL的狀態變量
//初始化地形、圖形等相關數據以便于繪制

// OpenGL場景初始化(光照、霧化等〕
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
return TRUE; // Initialization Went OK
}
BOOL CTrafficView::RenderScene()


{
wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC); // 尤為重要,MDI必不可少
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0,7,0,4,4,4,0,1,0); //視圖變換

// 場景繪制


glFlush();
::SwapBuffers(m_pDC->GetSafeHdc()); //交互緩沖區

return TRUE;
}
void CTrafficView::OnDestroy()


{
CView::OnDestroy();

// TODO: 在此處添加消息處理程序代碼
//刪除當前的RC
::wglMakeCurrent(NULL,NULL);
//刪除RC
::wglDeleteContext(m_hRC);

//刪除DC
if (m_pDC)
delete m_pDC;

}
void CTrafficView::OnSize(UINT nType, int cx, int cy)


{
CView::OnSize(nType, cx, cy);
//添加窗口縮放時的圖形變換函數,即視口變換
glViewport(0,0,cx,cy);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)cx/(GLfloat)cy,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

四、小結
前面介紹了VC下的OpenGL應用程序的開發框架,借助此框架,使得開發人員象使用Win32函數一樣來使用OpenGL提供的圖形庫函數,減輕了開發的難度,增強了程序代碼的魯棒性,因此對使用OpenGL進行圖形應用開發具有較高的借鑒意義。
posted on 2010-10-20 00:24
vic.MINg 閱讀(1000)
評論(0) 編輯 收藏 引用 所屬分類:
OpenGL