最近由於手機項目中需要用到OpenGL ES的知識,所以這段時間正在研究OpenGL的相關知識。因為OpenGL ES是OpenGL的剪裁版本,所以我直接從OpenGL入手,然后再去看OpenGL ES就很容易上手。從此篇開始,我將發表一系列文章來逐步深入介紹OpenGL的相關知識,開發我們可以使用VC6.0或.NET。
那么今天我要介紹的是OpenGL開發之旅基礎知識介紹,這很重要,會讓我們從整體上熟悉OpenGL的工作原理及過程。
1. 保持模式與立即模式:
通常情況下我們編寫3D圖形時可使用兩種不同的方法:
一種方法我們稱之為保持模式。在保持模式中,我們可以向編寫的API或是工具箱提供物體及場景的描述,然后圖形包就會在屏幕上創建這個圖像,我們需要做的就是提供命令去改變照相機或場景中其他物體的位置和觀察方向。對於我們開發者而言,我們創建的能夠對物體及場景的描述稱為場景圖,場景圖是什么呢?大家可能對這個名稱比較熟悉但卻不能說出它的准確含義。在通常情況下,場景圖是個有向無環圖的數據結構,它包含了場景中的所有物體以及這些物體之間相互的關系,實現了對物體及場景的描述。據了解,現在許多的游戲引擎及高層工具箱都使用了這種方法。這使得我們開發人員不需要對其渲染過程進行特別精細的控制,它只需要向圖形函數庫提供一個模型或是場景,圖形函數庫就會負責進行渲染,大大減輕了我們開發人員的工作量。
另一種方法我們稱之為立即模式。在立即模式中,我們不需要像保持模式一樣去提供一個模型或是場景,而是向圖形處理器發送命令,圖形處理器就會根據它的狀態及發送的命令產生立即的效果。查詢圖形學書籍得知,大多數保持模式中的API或場景圖在其內部使用一個立即模式的API執行實際的渲染任務。
2. OpenGL是什么?
OpenGL是一套應用程序編程接口(API),借助這個API我們開發人員就可以開發出對圖形硬件具有訪問的能力的程序。我們可以使用OpenGL開發出運行效率較高的圖形程序或游戲,因為OpenGL非常接近底層硬件並且OpenGL使得我們不必去關注圖形硬件的細節。既然我們開發人員不必關注圖形硬件的細節,那么我們需要關注什么呢?我們需要關注OpenGL如何繪制,按照專業術語就是根據物體的規格參數及相關屬性,借助虛擬照相機和光照生成一幅該物體的圖像。OpenGL程序與平台是無關的,所以OpenGL API中不包含任何輸入函數或窗口函數,原因是因為這兩種函數都要依賴於特定的平台,例如Windows,Linux或是其他系統。
OpenGL API是過程性的,不是描述性的,即OpenGL不是面向對象的,所以OpenGL無法利用面向對象的特性,例如重載,繼承等,但是我們可以使用面向對象的程序與OpenGL的實現進行鏈接就可以了。作為開發人員來說,我們不需要去描述場景的性質和外觀,而是去確定一些操作步驟,為些操作步驟是為實現一定圖形或圖像所服務的。我們在實現這些步驟時可以調用OpenGL中的一些命令,可以利用這些命令繪制點、直線、多邊形或是其它圖形,還可以調用這些命令實現光照、着色,動畫等各種效果。
OpenGL的實現可以是軟件實現,也可以是硬件實現。軟件實現是對OpengGL函數調用時作出的響應並創建二維或三維圖像的函數庫,那么硬件實現則是通過設置能夠繪制圖形或圖像的圖形卡驅動程序。一般來說,硬件實現要比軟件實現快得多。我們都應該熟悉,在Windows上,是由圖形設備接口將圖形或圖像顯示在屏幕上或是其他顯示設備上的。OpenGL的實現就軟件實現來說,在Windows上會根據程序命令的要求,生成相應的圖形或圖像,然后會將這個圖形或圖像移交給圖形設備接口,由圖形設備接口將圖形或是圖像顯示在我們的屏幕上或是其他顯示設備。這樣一說,我們可能會明白一點OpengGL原來是在應用程序和圖形設備接口之間運作,但我感覺還不能准確地這樣說。大家看下下面的圖對OpenGL的工作原理可能會理解得更明白一點:
上圖是OpenGL的軟件實現的工作原理。需要注意的是上圖中的構造圖形是通過軟件進行構造的。
OpenGL的硬件實現與軟件實現稍微有些不同,硬件實現是將OpenGL的調用傳遞給硬件驅動程序,而硬件驅動程序不會將生成的圖形或圖像傳遞給圖形設備接口,而是直接與顯示設備通信,直接將圖形或圖像結果傳遞給顯示器或其他顯示設備。如下圖所示:
OpenGL在繪制圖形時是基於一個被稱為流水線模型的模式。也就是說其中的幾何圖形在程序中通過描述空間位置或頂點來指定其形狀並由程序生成,這些頂點在流經一系列模塊時,每個模塊在圖形的基本組成部分(在這里稱為圖元)經過時對其實施一種或多種操作。模塊負責對流經的圖元實施一種或多種操作變換,例如:旋轉、平移、縮放及對攝像機進行定位等。
3. OpenGL的組成
OpenGL中包含許多對圖形圖像處理的函數,主要包括以下幾種:
圖元函數:指定要生成圖形或圖像的圖元。主要有兩種類型,一種是繪制二維或三維的幾何圖元,如點,線,多邊形等;另一種是離散型的實體,例如:位圖。
屬性函數:屬性函數主要是控制圖元的外觀及樣式,例如:對圖元的顏色、線型、光照及紋理等效果處理。
觀察函數:觀察函數主要是對攝像機屬性的操作。我們可以操作攝像機顯示圖形或圖像近距或是遠距效果。
控制函數:能夠讓我們啟用或是彬各種OpenGL的特性。
查詢函數:可以讓我們查詢OpenGL狀態變量的值。
輸入與窗口控制函數:這個本身不屬於OpenGL,但是由於我們會經常在程序中輸入輸出或是窗口控制操作,所以,這些函數還是比較重要的。
OpenGL函數庫一般包含在兩個庫中,分別稱為GL或GLU。GL是OpenGL的核心庫,包含必需的OpenGL函數。GLU是OpenGL的實用庫,包含許多的新函數。下面的代碼顯示了許多的Windows程序包含的典型頭文件:
#include <GL/gl.h> #include <GL/glu.h> #include <windows.h>
但是為了實現和窗口系統的交互,一般使用如下代碼引用頭文件:
#include <GL/glut.h>
GLUT表示OpenGL工具箱,體現了現代窗口系統所共有的功能函數庫。GLUT的目的就是隱藏平台的細節,glut.h已經包含了gl.h和glu.h。使用GLUT是因為OpenGL沒有包含輸入和窗口命令,而輸入和窗口命令是由平台所決定的,與平台的相關性較大。但是前面說過,OpenGL是與平台無關的,也就是說OpenGL是跨平台的。這樣設計人員就需要專門設計一個需要和窗口系統進行交互的函數庫。
為了能使OpenGL代碼更易於從一個平台移植到另一個平台,OpenGL定義了它自己的數據類型,這些數據類型都可以映射到相應的C語言數據類型中。下圖顯示其映射關系:
4. 開發語言與編程約定
我們開發OpenGL目前最流行的做法是OpenGL的C語言綁定,當然也可以使用其它平台或語言,例如.NET、JAVA、Python、Perl等。我會在下一篇文章中介紹如何配置相應的環境。
我們以后會見到OpenGL的函數多是以gl開頭,因為OpenGL的函數遵循一定的命名約定,它可以告訴我們這個函數來自哪個函數庫,並且還可以告訴我們這個函數的參數個數和類型。
OpenGL的函數是采用以下的書寫格式:
<函數庫前綴><根命令><可選的參數數量><可選的參數類型>
以下是一個函數標注圖:
5. 坐標系與變換
在開發OpenGL程序時,需要用到兩個坐標系。一個稱為對象坐標系,另一個稱為世界坐標系。
第一個坐標系是我們在開發中使用的坐標系;第二個坐標系又稱為窗口坐標系或屏幕坐標系,在這個坐標系中的單位是像素。
在繪制的過程中,OpenGL會自動實現從對象到窗口坐標系的轉換,所需要的信息是屏幕中顯示窗口的尺寸和用戶希望顯示對象空間的大小。OpenGL中所需要的坐標系變換由兩個矩陣決定,即模型視圖矩陣和投影矩陣,這些矩陣是OpenGL的狀態的一部分。設置這兩種矩陣的典型步驟包括以下三個步驟:
(1) 指定我們希望修改的矩陣。
(2) 將矩陣設為單位矩陣。
(3) 修改當前矩陣為用戶期望的矩陣。
以上三個步驟分別對應以下代碼:
glMatrixMode(GL_PROJECTION) glLoadIdentity(); gluortho2D(-1.0,1.0,-1.0,1.0)
6. 圖元及屬性
圖元是圖形系統中常用的基本實體,主要是指:點,線,直線,多邊形,位圖和像素,我們的2D或3D圖形都可由這幾個基本的圖元來繪制。前面四個稱為幾何圖元,主要是構建幾何圖形;后面二個稱為非幾何圖元。OpenGL在處理幾何圖元和非幾何圖元的方式差別比較大。
每個圖元都有自己的屬性,屬性決定了由OpenGL顯示的方式。比如多邊形的顏色,形狀,線條的粗細等。
我們在繪制基本的圖形時,總是以glBegin()函數開始,而以glEnd()函數結束,針對不同的圖形,glBegin()函數中的參數不一樣。如下所示是繪制兩條直線段:
glBegin(GL_LINES); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd();
OpenGL在繪制圖形時有很多功能,比如:光照、消隱、紋理映射等,每一種功能都將影響繪制處理的速度,在我們的程序中可單獨的啟用或是禁用某些功能,在不使用時要將其禁用掉以使我們的程序更加高效。以下是啟用或是禁用某項功能的代碼:
void glEnable(GLenum feature) void glDisable(GLenum feature) //啟用點划模式 glEnable(GL_LINE-STIPPLE)
7. 狀態的保存
OpenGL在內部就是一個狀態機,函數調用會修改其內部的狀態,OpenGL的狀態決定了圖元的行為和繪制方式。我們對圖元的屬性和其他狀態變量所進行的全部修改,例如模型視圖矩陣和投影矩陣,都會改變當前的狀態。在OpenGL中提供了兩種類型的堆棧,可將當前狀態保存在堆棧中,以便以后使用。
矩陣堆棧可用於保存投影矩陣和模型視圖矩陣。每種類型的堆棧只能用來容納相應類型的矩陣。所使用的矩陣由當前矩陣模式(GL_MODELVIEW或GL_PROJECTION)所決定的。可用函數glPushMatrix()和glPopMatrix()使矩陣入棧或出棧。
矩陣堆棧的主要作用:一是在構建層次模型時,使用堆棧來遍歷這些層次模型的樹型數據結構;二是在進行繪制時可以回到先前的視圖,而不需要我們重新計算繪制。我們會在開發過程中常看到以下代碼:
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glPopMatrix();
需要注意的是,入棧操作和出棧操作必須成對使用;一次出棧必須與一次入棧對應。在層次系統中,如果這對操作沒有正確的成對出現的話,將使堆棧處於一種不可預知的狀態。
8. 單緩沖與雙緩沖
我們在構建圖形窗口時經常也會經常看到以下的一行代碼:
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
這行代碼告訴我們在創建時使用何種類型的顯示模式。GLUT_SINGLE表示使用一個單緩沖的窗口;GLUT_RGBA表示使用RGBA顏色模式。
單緩沖窗口意味着所有的繪圖命令都是在被顯示的窗口上執行的。另一種顯示模式是雙緩沖窗口,繪圖命令實際上是在一個屏幕之外的緩沖區中執行的,然后快速反應的交換到窗口的視圖上進行顯示。我們經常用雙緩沖模式開發具有動畫效果的程序,這樣會提高我們程序的執行效率。
9. 一個簡單的示例
好了,上面說了那么多,有的人可能想知道OpenGL的程序到底是什么樣的,主框架是什么?大家如果有C語言編程經驗的一眼就能看出來它的結構。
接下來讓我們看一個運行VC++環境中的OpenGL示例,代碼如下:
#include "StdAfx.h" #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut.h> /* glut.h 包含 gl.h和glu.h*/ #endif void display(void) { // 清除顏色緩沖區 glClear(GL_COLOR_BUFFER_BIT); // 繪制矩形 glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); // 執行緩沖區 glFlush(); } int main(int argc, char** argv) { // 初始化窗口 glutInit(&argc,argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("繪制矩形"); glutDisplayFunc(display); glutMainLoop(); return 0; }
這是運行在VC6.0中的一個示例,在創建項目時需要注意創建的是控制台項目。
怎么樣?main函數中的代碼你能看懂它的意思嗎?我相信對大家不是難事。下面看來簡單的解釋。
(1) glutCreateWindow("繪制矩形")是創建一個標題是"繪制矩形"的窗口。
(2) glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB)函數在上面已經說過了。
(3) glutDisplayFunc(display); 這行代碼是將前面定義的display()函數確定為顯示回調函數。也就是說在窗口需要被繪制時,GLUT將會調用這個函數。比如說,當窗口第一次顯示或是窗口大小改變的時候,或是窗口從被覆蓋的狀態中恢復時,就會發生這個調用。這也是我們放置OpenGL渲染函數調用的地方。
(4) glutInitWindowSize(500,500)和glutInitWindowPosition(0,0)則是分別設置窗口的大小和位置。
(5) glutMainLoop() 這個函數很重要。這個函數啟動了GLUT框架的運行。該函數一經調用便不再返回,直到程序的結束。所以,該函數在我們的程序中只調用一次,它能處理操作系統中特定的消息及擊鍵等事件操作,直到我們的程序結束。
以上示例的效果是在窗體中繪制一個白色的矩形,效果圖如下所示:
該程序演示了在OpenGL中使用GLUT創建窗口的基本原理。
以上便是OpenGL開發中的基本原理和基礎知識。
最后,希望轉載的朋友能夠尊重作者的勞動成果,加上轉載地址:http://www.cnblogs.com/hanyonglu/archive/2012/05/13/2498110.html 謝謝。
示例下載:/Files/hanyonglu/OpenGL/MyOpenGLDemo4.rar
完畢。^_^