OpenGL ES一 – 基本概念

我曾寫過一些文章介紹iPhone OpenGL ES編程,但大部分針對的是已經至少懂得一些3D編程知識的人。
作爲起點,請下載我的<a href="http://www.iphone-geek.cn/wp-content/uploads/2009/12/Empty_OpenGL_ES_Application.zip" color:="" rgb(63,="" 63,="" 175);="" "="" style="word-wrap: break-word; color: rgb(0, 51, 153); text-decoration: none; font-family: Tahoma, Simsun; font-size: 14px; line-height: 25px;">OpenGL Xcode項目模板,而不要使用Apple提供的模板。你可以解壓到下面位置安裝此模板:
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application/

已經有大量有關OpenGL的好教程和書籍。但是,卻沒有多少是關於OpenGL ES,而且沒有(至少在我撰寫此文時)是專門針對學習iPhone上3D編程的。因爲大部分有關學習OpenGL的材料是從所謂“直接模式(direct mode)”開始的,而OpenGL ES並不支持此模式,對於沒有3D背景知識的iPhone開發者而言,使用現有的書籍和教程是十分困難的。爲滿足一些開發者的要求,我決定撰寫一個針對3D初學者的博文系列。這是此係列的第一篇文章。

OpenGL 數據類型

首先我們要討論的是OpenGL的數據類型。因爲OpenGL是一個跨平臺的API,數據類型的大小會隨使用的編程語言以及處理器(64位,32位,16位)等的不同而不同,所以OpenGL定義了自己的數據類型。當傳遞數據到OpenGL時,你應該堅持使用這些OpenGL的數據類型,從而保證傳遞數據的尺寸和精度正確。不這樣做的後果是可能會導致無法預料的結果或由於運行時的數據轉換造成效率低下。不論平臺或語言實現的OpenGL都採用這種方式定義數據類型以保證在各平臺上數據的尺寸一致,並使平臺間OpenGL代碼移植更爲容易。

下面是OpenGL的各種數據類型:

  • GLenum: 用於GL枚舉的無符號整型。通常用於通知OpenGL由指針傳遞的存儲於數組中數據的類型(例如,GL_FLOAT用於指示數組由GLfloat組成)。
  • GLboolean: 用於單布爾值。OpenGL ES還定義了其自己的“真”和“假”值(GL_TRUE和GL_FALSE)以避免平臺和語言的差別。當向OpenGL傳遞布爾值時,請使用這些值而不是使用YES或NO(儘管由於它們的定義實際沒有區別,即使你不小心使用了YES或NO。但是,使用GL-定義值是一個好的習慣。)
  • GLbitfield: 用於將多個布爾值(最多32個)打包到單個使用位操作變量的四字節整型。我們將在第一次使用位域變量時詳細介紹,請參閱 wikipedia
  • GLbyte: 有符號單字節整型,包含數值從-128 到 127
  • GLshort: 有符號雙字節整型,包含數值從−32,768 到 32,767
  • GLint: 有符號四字節整型,包含數值從−2,147,483,648 到 2,147,483,647
  • GLsizei: 有符號四字節整型,用於代表數據的尺寸(字節),類似於C中的size_t
  • GLubyte: 無符號單字節整型,包含數值從0 到 255。
  • GLushort: 無符號雙字節整型,包含數值從0 到 65,535
  • GLuint: 無符號四字節整型,包含數值從0 到 4,294,967,295
  • GLfloat: 四字節精度IEEE 754-1985 浮點數
  • GLclampf: 這也是四字節精度浮點數,但OpenGL使用GLclampf特別表示數值爲0.0 到 1.0
  • GLvoid:void值用於指示一個函數沒有返回值,或沒有參數
  • GLfixed定點數 使用整型數存儲實數。由於大部分計算機處理器在處理整型數比處理浮點數快很多,這通常是對3D系統的優化方式。但因爲iPhone具有用於浮點運算的矢量處理器,我們將不討論定點運算或GLfixed數據類型。
  • GLclampx: 另一種定點型,用於使用定點運算來表示0.0 到 1.0之間的實數。正如GLfixed,我們不會討論或使用它。

OpenGL ES (至少iPhone目前所使用的版本)不支持8字節(64位)數據類型,如long或double。OpenGL 其實具有這些大型數據類型,但考慮到大部分嵌入式設備屏幕尺寸以及可能爲它們所寫的程序類型而且使用它們有可能對性能造成不利的影響,最後的決定是在OpenGL ES中排除這些數據類型。

點或頂點

3D圖像的最小單位稱爲 點(point) 或者 頂點vertex。它們代表三維空間中的一個點並用來建造更復雜的物體。多邊形就是由點構成,而物體是由多個多邊形組成。儘管通常OpenGL支持多種多邊形,但OpenGL ES只支持三邊形(即三角形)。

如果你回憶高中學過的幾何學,你可能會記得所謂笛卡爾座標。 基本概念是在空間中任選一點,稱作原點。 然後你可以通過參照原點並使用三個代表三維的數值指定空間中的任意一點,座標是由三個想象的通過原點線表示的。從左至右的想象直線叫x-軸。沿着x-軸從左至右數值變大,向左移動數值變小。原點左方x爲負值,右邊爲正值。另外兩軸同理。沿y軸向上,y值增加,向下y值減小。原點上方y爲正,原點下方爲負。對於z軸,當物體離開觀察者,數值變小,向觀察者移動(或超出觀察者),數值變大。原點前方z值爲正,原點之後爲負。下圖幫助說明了這一點:

Note: iPhone上另一種繪圖框架Core Graphics使用了稍微不同的座標系統,當向屏幕上方移動時y值減小,而向下移動y值增加。

沿各軸增加或減小的數值是以任意刻度進行的 – 它們不代表任何真實單位,如英尺,英寸或米等。你可以選擇任何對你的程序有意義的刻度。如果你想設計的遊戲以英尺爲單位,你可以那樣做。如果你希望單位爲毫米,同樣可行。OpenGL不管它對最終用戶代表什麼,只是將它作爲單位處理,保證它們具有相同的距離。

由於任何物體在三維空間中的方位可以由三個數值表示,物體的位置通常在OpenGL中由使用一個三維數組的三個GLfloat變量表示,數組中的第一項(索引0)爲x位置,第二項(索引1)爲y位置,第三項(索引2)爲z位置。下面是一個創建OpenGL ES頂點的簡單例子:

    GLfloat vertex[3];
    vertex[0] = 10.0;       // x
    vertex[1] = 23.75;      // y
    vertex[2] = -12.532;    // z

在OpenGL ES中,通常將場景中所有構成所有或部分物體的提交爲頂點數組。一個頂點數組是包括場景中部分或所有頂點數據的簡單數組。我將在系列的下一篇教程中討論,有關頂點數組要記住的是它們的大小是基於呈現的頂點數乘以三(三維空間繪圖)或二(二維空間繪圖)。所以一個包含六個三維空間中的三角形的頂點數組由54個GLfloat組成,因爲每個三角形有三個頂點,而每個頂點有三個座標,即6 x 3 x 3 = 54。

處理所有這些GLfloat是很痛苦的事情。幸運的是,有一個容易的方法。我們可以定義一個數據結構了保存多個頂點,像這樣:
typedef struct { 
    GLfloat x;
    GLfloat y;
    GLfloat z;
} Vertex3D;

通過這樣做,我們的代碼可讀性更強:

Vertex3D vertex;
vertex.x = 10.0;
vertex.y = 23.75;
vertex.z = -12.532;


現在由於Vertex3D由三個GLfloat組成,向Vertex3D傳遞指針與向數組傳遞一個包含三個GLfloat的數組的指針完全一樣。對於電腦而言毫無分別;兩者具有同樣的尺寸和同樣的字節數以及OpenGL需要的同樣的順序。將數據分組到數據結構只是讓程序員感到更容易,處理起來更方便。如果你下載了文章開頭處的Xcode模板,你會發現此數據結構以及我後面將討論的各種函數都定義在文件OpenGLCommon.h中。還有一個內聯函數用於創建單個頂點:

static inline Vertex3D Vertex3DMake(CGFloat inX, CGFloat inY, CGFloat inZ)
{
    Vertex3D ret;
    ret.x = inX;
    ret.y = inY;
    ret.z = inZ;
    return ret;
}

如果你回憶起幾何學(如果不記得也不要緊)的內容,你會知道空間中兩點間的距離是使用下面公式計算的:

我們可以在一個簡單的內聯函數中實現這個公式來計算三維空間中任何兩點間的直線距離:

static inline GLfloat Vertex3DCalculateDistanceBetweenVertices (Vertex3D first, Vertex3D second)
{
    GLfloat deltaX = second.x - first.x;
    GLfloat deltaY = second.y - first.y;
    GLfloat deltaZ = second.z - first.z;
    return sqrtf(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ );
};


三角形

由於OpenGL ES僅支持三角形,因此我們可以通過創建一個數據結構將三個頂點組合成一個三角形物體。

typedef struct {
    Vertex3D v1;
    Vertex3D v2;
    Vertex3D v3;
} Triangle3D;

一個 Triangle3D實際上與一個九個GLfloat構成的數組是完全一樣的,因爲我們通過頂點和三角形而不是GLfloat數組來構建物體,所以它能幫助我們更容易地處理我們的代碼。

然而關於三角形你需要知道更多的事情。在OpenGL中有一個概念叫卷繞(winding), 它表示頂點繪製的次序是重要的。不像真實世界中的物體,OpenGL中的多邊形通常都不會有兩面。它們只有一面,被當做front face(前面), 三角形只有其front face面對觀察者時纔可見。可以設置OpenGL將多邊形作爲兩面處理,但默認狀態下,三角形只有一個可見面。通過知道哪一個面是多邊形的前面或可見面,才能使OpenGL只做一半的計算。

儘管有時多邊形也可以獨立存在,需要繪製其背面,但通常三角形是一個大物體的一部分,其面對物體內部的一面永遠也不可見。不被繪製的一面稱爲backface(背面),OpenGL是通過觀察頂點的繪製次序來確定front face和backface的。以反時針次序繪製頂點的構成的面是frontface(默認,可以改變)。由於OpenGL可以很容易確定哪個三角形對用戶可見,所以它使用了一種稱爲Backface Culling(隱面消除) 的技術來避免繪製視窗中多邊形的不可見面。下一篇文章將討論視窗,現在你可將其想象成一個虛擬攝像或觀察OpenGL世界的虛擬窗口。

上圖中,左邊青色的三角形是backface,因此將不可見。而右方的三角形是frontface,所以將被繪製。

本系列的下一篇文章將設定一個OpenGL的虛擬世界並使用Vertex3D 和 Triangle3D進行一些基本繪圖。再後,我們將討論變換,它使用線性代數在虛擬世界中移動物體。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章