NeHe OpenGL教程 學習筆記1

[size=xx-large]如果你[/size]
和我一樣剛剛學習OpenGL,跟着教程走了一遍但還是感覺對一些東西的理解很模糊,你可以繼續往下看,我們一起來探討。如果不是,很抱歉浪費了您的時間。

[size=small][color=gray]PS:看了快三個星期的OpenGL了,NeHe的教程看似很簡單(因爲你只要照着敲就能做出一些很有意思的小demo出來),不過有很多地方需要自己去弄懂和理解的,於是就先把自己的理解寫下來。所以,純屬個人簡介,如有錯誤,歡迎指正。[/color][/size]

[size=xx-large]Lesson 1 窗口的建立[/size]
[size=large]1描述[/size]
創建一個window窗口,以此作爲windows的載體。
Nehe的窗口框架搭建的非常好,理解的難點隨之出現。這一課有許多關於OpenGL視圖環境的設置函數,這裏暫且略過(因爲我現在還說不清),主要描述一下windows窗口的創建以及淺談一些消息機制吧。
[size=large]2流程[/size]
1.定義一個WNDCLASS窗口結構體(這個變量名貌似取得不是很好啊)

WNDCLASS wc; // 窗口類結構

2.設置WNDCLASS的屬性,也就是確定窗體的樣式風格。
這裏有一個很重要的屬性,也就是WNDCLASS的lpfnWndProc字段,這裏指定窗口的消息處理函 數,後面會再次提到。

wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // 移動時重畫,併爲窗口取得DC
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc處理消息
wc.cbClsExtra = 0; // 無額外窗口數據
wc.cbWndExtra = 0; // 無額外窗口數據
wc.hInstance = hInstance; // 設置實例
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // 裝入缺省圖標
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 裝入鼠標指針
wc.hbrBackground = NULL; // GL不需要背景
wc.lpszMenuName = NULL; // 不需要菜單
wc.lpszClassName = "OpenG"; // 設定類名字

3.註冊該窗口
if (!RegisterClass(&wc))						// 嘗試註冊窗口類
{
MessageBox(NULL,"註冊窗口失敗","錯誤",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 退出並返回FALSE
}

4.創建窗口,返回一個HWND類型的窗口句柄,這個很重要,各種資源的獲取以及屬性的設置都要用到它。

hWnd=CreateWindowEx( dwExStyle, // 擴展窗體風格
TEXT("OpenGL"), // 類名字
(LPCWSTR)title, // 窗口標題
dwStyle | // 必須的窗體風格屬性
WS_CLIPSIBLINGS | // 必須的窗體風格屬性
WS_CLIPCHILDREN, // 必須的窗體風格屬性
0, 0, // 窗口位置
WindowRect.right-WindowRect.left, // 計算調整好的窗口寬度
WindowRect.bottom-WindowRect.top, // 計算調整好的窗口高度
NULL, // 無父窗口
NULL, // 無菜單
hInstance, // 實例
NULL))

5.顯示窗口
ShowWindow(hWnd,SW_SHOW);						// 顯示窗口


至此,一個基本的窗口就已經搭建好並顯示成功了,但是貌似和OpenGL一點關係也沒有。NeHe的窗口創建和OpenGL的舞臺搭建基本上是揉在一起的,所以我將其分開。下面是OpenGL的舞臺搭建

1.創建像素格式,這個可以看做是OpenGL與windows窗口的連接紐帶,像素格式PIXELFORMATDESCRIPTOR告訴windows窗口你需要提供一個相應的格式來滿足OpenGL的顯示要求

static PIXELFORMATDESCRIPTOR pfd= // /pfd 告訴窗口我們所希望的東東,即窗口使用的像素格式
{
sizeof(PIXELFORMATDESCRIPTOR), // 上述格式描述符的大小
1, // 版本號
PFD_DRAW_TO_WINDOW | // 格式支持窗口
PFD_SUPPORT_OPENGL | // 格式必須支持OpenGL
PFD_DOUBLEBUFFER, // 必須支持雙緩衝
PFD_TYPE_RGBA, // 申請 RGBA 格式
bits, // 選定色彩深度
0, 0, 0, 0, 0, 0, // 忽略的色彩位
0, // 無Alpha緩存
0, // 忽略Shift Bit
0, // 無累加緩存
0, 0, 0, 0, // 忽略聚集位
16, // 16位 Z-緩存 (深度緩存)
0, // 無蒙板緩存
0, // 無輔助緩存
PFD_MAIN_PLANE, // 主繪圖層
0, // Reserved
0, 0, 0 // 忽略層遮罩
};

2.獲得設備描述表,設備描述表...好吧~我第一次看的時候也懵了,什麼東西?我們可以將設備理解成資源,很多資源的分配和獲得都要用到它,就我目前的理解,將它理解成資源管理器也差不多,這裏的資源可以包括內存空間和窗口的設備(又是設備,找不到其他詞了),比如:OpenGL裏面可以將需要重複繪製的圖像模型先存放在內存裏(有點像是享元模式),也就是常說的顯示列表,這樣就可以提高運行效率,和數據就需要設備描述表來進行創建。

if (!(hDC=GetDC(hWnd))) // 取得設備描述表了麼?
{
KillGLWindow(); // 重置顯示區
MessageBox(NULL,"不能創建一種相匹配的像素格式","錯誤",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}

3.檢測像素格式並進行設置

if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Windows 找到相應的象素格式了嗎?
{
KillGLWindow(); // 重置顯示區
MessageBox(NULL,TEXT("不能創建一種相匹配的像素格式"),TEXT("錯誤"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}

if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // 能夠設置象素格式麼?
{
KillGLWindow(); // 重置顯示區
MessageBox(NULL,TEXT("不能設置像素格式"),TEXT("錯誤"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}

4.渲染描述表?暫時還沒有用到過,以後瞭解之後會重寫這裏
做到這裏,OpenGL的舞臺環境已經基本搭建完成

if (!(hRC=wglCreateContext(hDC))) // 能否取得OpenGL渲染描述表?
{
KillGLWindow(); // 重置顯示區
MessageBox(NULL,TEXT("不能創建OpenGL渲染描述表"),TEXT("錯誤"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}

if(!wglMakeCurrent(hDC,hRC)) // 嘗試激活着色描述表
{
KillGLWindow(); // 重置顯示區
MessageBox(NULL,TEXT("不能激活當前的OpenGL渲然描述表"),TEXT("錯誤"),MB_OK|MB_ICONEXCLAMATION);
return FALSE; // 返回 FALSE
}

5.OpenGL初始化,一般封裝成一個函數如initGL在主體程序運行前進行調用。這裏是一些基本的參數,一些其他的初始化工作,如3D世界的數據讀入等操作,也可以寫在這個裏面。

int InitGL(GLvoid)
{
glShadeModel(GL_SMOOTH);//陰影模式
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);//背景色
//深度緩存設置和測試
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
//讓系統修正透視
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
buildFont();
return TRUE;
}



[size=xx-large]Extra 消息機制[/size]
雖然教程中沒怎麼說明,但是我覺得這是一個很重要的機制。

[size=large]描述[/size]
windows的消息機制其實是一個不斷循環的過程,對於窗口主體程序而言,其過程可以大體總結爲 監聽消息---取得消息---處理消息(消息處理函數)---監聽消息......
[size=large]流程[/size]
[size=medium]1.監聽消息&取得消息[/size]
爲什麼要一起講?這裏要討論兩個函數
[color=gray][size=medium]PeekMessage和GetMessage[/size][/color]
PeekMessage:無論應用程序消息隊列是否有消息,PeekMessage函數都立即返回,程序得以繼續執行
後面的語句(無消息則執行其它指令,有消息時一般要將消息派發出去,再執行其它
指令)。
GetMessage:函數只有在消息對立中有消息時返回,隊列中無消息就會一直等,直至下
一個消息出現時才返回。在等的這段時間,應用程序不能執行任何指令。
所以GetMessage對消息有一種監聽的感覺,而PeekMessage就只是一味的取數據而已了。

在NeHe的教程中使用的是PeekMessage,而在《Windows程序設計》一書中則是使用的GetMessage,原因是,在NeHe中,主函數的消息循環不僅僅起到接收外部消息的作用,它還是OpenGL動畫的一個“始終”,每循環一次就會重新繪製一次從而產生動態效果。

while(!done) // 保持循環直到 done=TRUE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // 有消息在等待嗎?
{
if (msg.message==WM_QUIT) // 收到退出消息?
{
done=TRUE; // 是,則done=TRUE
}
else // 不是,處理窗口消息
{
TranslateMessage(&msg); // 翻譯消息
DispatchMessage(&msg); // 發送消息
}
}
else // 如果沒有消息
{
// 繪製場景。監視ESC鍵和來自DrawGLScene()的退出消息
if (active) // 程序激活的麼?
{
if (keys[VK_ESCAPE]) // ESC 按下了麼?
{
done=TRUE; // ESC 發出退出信號
}
else // 不是退出的時候,刷新屏幕
{
DrawGLScene(); // 繪製場景
SwapBuffers(hDC); // 交換緩存 (雙緩存)

}
}

if (keys[VK_F1]) // F1鍵按下了麼?
{
keys[VK_F1]=FALSE; // 若是,使對應的Key數組中的值爲 FALSE
KillGLWindow(); // 銷燬當前的窗口
fullscreen=!fullscreen; // 切換 全屏 / 窗口 模式
// 重建 OpenGL 窗口
if (!CreateGLWindow(TEXT("NeHe's OpenGL 程序框架"),640,480,16,fullscreen))
{
return 0; // 如果窗口未能創建,程序退出
}
}
}
}

看到一上代碼,注意到DrawGLScene(),SwapBuffers(hDC)兩個函數,如果將PeekMessage換成GetMessage的話,湖面應該就動不了了吧。
2.處理消息
任然是上面的代碼,當獲得消息以後,則會執行下面兩行代碼
TranslateMessage(&msg);				// 翻譯消息
DispatchMessage(&msg); // 發送消息

TranslateMessage做的事情,《windows程序設計》寫的是進行鍵盤翻譯,第六章有深入探討(懶得去看哦),我猜大概就是將鍵盤的消息裝換成相應的ASCLL碼的值吧,“望文生義”有時還是很必要的。
DispatchMessage(&msg)做的事情則是將消息交給在WNDCLASS定義階段綁定的那個消息響應函數(還記得不?不記得翻上去看看那吧),由響應函數進行處理,下面貼出一部分

LRESULT CALLBACK WndProc( HWND hWnd, // 窗口的句柄
UINT uMsg, // 窗口的消息
WPARAM wParam, // 附加的消息內容
LPARAM lParam) // 附加的消息內容
{
switch (uMsg) // 檢查Windows消息
{
case WM_ACTIVATE: // 監視窗口激活消息
{
if (!HIWORD(wParam)) // 檢查最小化狀態
{
active=TRUE; // 程序處於激活狀態
}
else
{
active=FALSE; // 程序不再激活
}

return 0; // 返回消息循環
}
//各種消息類別的處理....省略
}

// 向 DefWindowProc傳遞所有未處理的消息。
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

DefWindowProc(hWnd,uMsg,wParam,lParam)是將我們不關心的消息扔回去讓缺省的消息響應函數處理,比如WM_CLOSE消息什麼的....

[size=x-large]其他[/size]
首先,此文意在梳理學習過程中的疑問以及整合知識的結構流程。
最後附上我修改過後的OpenGL窗口文件,可在[color=cyan]VS2010[/color]下運行,我一般將它作爲每次課程的“白板”,希望有所幫助。
Nehe中文教程地址[url]http://www.owlei.com/DancingWind/[/url]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章