1. 程式人生 > >OpenGL開發基礎知識介紹

OpenGL開發基礎知識介紹

最近由於手機專案中需要用到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開發中的基本原理和基礎知識。

原文出處: