iOS 開發 OpenGL 新手入門


一、搭建開發環境

1、打開XCODE,新建一個工程

選擇:IOS-->ApplicationàSingle View Application模板。

取名為“HelloOpenGL”,勾選“UseStoryboards”,然后創建。

從零開始構建IOS的OpenGL應用(-) - mississi - mississi的博客

 
  

2、添加必要的框架

在“Build Phases”欄,添加進三個框架:

從零開始構建IOS的OpenGL應用(-) - mississi - mississi的博客

3、修改viewController.h

添加“#import <GLKit/glkit.h>”,並將它修改為繼承“GLKViewController”。


從零開始構建IOS的OpenGL應用(-) - mississi - mississi的博客
 

  

4、修改“view”的類

雙擊“MainStoryboard.storyboard”展開,選擇“view”。

從零開始構建IOS的OpenGL應用(-) - mississi - mississi的博客

 
  

然后,在其“Identity Inspector”中,將它的類改為“GLKView”。

從零開始構建IOS的OpenGL應用(-) - mississi - mississi的博客

 

 至此,OpenGL環境已基本搭建出來了。運行,應該不會報錯,盡管它目前仍是黑屏。

 

二、開始堆代碼

基本上,所有的代碼都在“ViewController.m”中寫。

1、添加全局屬性聲明

從零開始構建IOS的OpenGL應用(二) - mississi - mississi的博客

 

當然,還得在實現部分補足“@synthesize context;”和“@synthesize effect;”。

 

2、添加一組頂點數據

這是一個正方形頂點的數組,實際上它是由二個三角形接合而成的。

從零開始構建IOS的OpenGL應用(二) - mississi - mississi的博客

 每行頂點數據的排列含義是:

頂點X、頂點Y,頂點Z、法線X、法線Y、法線Z、紋理S、紋理T。

在后面解析此數組時,將參考此規則。

頂點位置用於確定在什么地方顯示,法線用於光照模型計算,紋理則用在貼圖中。

一般約定為“頂點以逆時針次序出現在屏幕上的面”為“正面”。

世界坐標是OpenGL中用來描述場景的坐標,Z+軸垂直屏幕向外,X+從左到右,Y+軸從下到上,是右手笛卡爾坐標系統。我們用這個坐標系來描述物體及光源的位置。

 

三、初始化OpenGL環境

1、基本的初始化代碼

在“viewDidLoad”方法內,補充初始化代碼:

從零開始構建IOS的OpenGL應用(三) - mississi - mississi的博客

 

第一部分:使用“ES2”創建一個“EAGLContext”實例。

第二部分:將“view”的context設置為這個“EAGLContext”實例的引用。並且設置顏色格式和深度格式。

第三部分:將此“EAGLContext”實例設置為OpenGL的“當前激活”的“Context”。這樣,以后所有“GL”的指令均作用在這個“Context”上。隨后,發送第一個“GL”指令:激活“深度檢測”。

第四部分:創建一個GLK內置的“着色效果”,並給它提供一個光源,光的顏色為綠色。

 

2、運行

現在應該是粉紅色屏幕了(目前場景仍是空的),說明初始化過程沒問題。

 

四、將頂點數據寫入通用的頂點屬性存儲區

1、寫入過程

首先將數據保存進GUP的一個緩沖區中,然后再按一定規則,將數據取出,復制到各個通用頂點屬性中。

注:如果頂點數據只有一種類型(如單純的位置坐標),換言之,在讀數據時,不需要確定第一個數據的內存位置(總是從0開始),則不必事先保存進緩沖區。

 

2、頂點數組保存進緩沖區

從零開始構建IOS的OpenGL應用(四) - mississi - mississi的博客

 

這幾行代碼表示的含義是:聲明一個緩沖區的標識(GLuint類型)à讓OpenGL自動分配一個緩沖區並且返回這個標識的值à綁定這個緩沖區到當前“Context”à最后,將我們前面預先定義的頂點數據“squareVertexData”復制進這個緩沖區中。

注:參數“GL_STATIC_DRAW”,它表示此緩沖區內容只能被修改一次,但可以無限次讀取。

 

3、將緩沖區的數據復制進通用頂點屬性中

從零開始構建IOS的OpenGL應用(四) - mississi - mississi的博客

  

首先,激活頂點屬性(默認它的關閉的)。“GLKVertexAttribPosition”是頂點屬性集中“位置Position”屬性的索引。

頂點屬性集中包含五種屬性:位置、法線、顏色、紋理0,紋理1。

它們的索引值是0到4。

激活后,接下來使用“glVertexAttribPointer”方法填充數據。

參數含義分別為:

頂點屬性索引(這里是位置)、3個分量的矢量、類型是浮點(GL_FLOAT)、填充時不需要單位化(GL_FALSE)、在數據數組中每行的跨度是32個字節(4*8=32。從預定義的數組中可看出,每行有8個GL_FLOAT浮點值,而GL_FLOAT占4個字節,因此每一行的跨度是4*8)。

最后一個參數是一個偏移量的指針,用來確定“第一個數據”將從內存數據塊的什么地方開始。

 

4、繼續復制其他數據

在前面預定義的頂點數據數組中,還包含了法線和紋理坐標,所以參照上面的方法,將剩余的數據分別復制進通用頂點屬性中。

從零開始構建IOS的OpenGL應用(四) - mississi - mississi的博客

 

原則上,必須先“激活”某個索引,才能將數據復制進這個索引表示的內存中。

因為紋理坐標只有兩個(S和T),所以上面參數是“2”。

 

五、執行渲染循環

萬事具備,現在可以讓OpenGL顯示一些東西了。

在GLKit框架中,盡管OpenGL的行為,是由“GLKViewController”和“GLKView”聯合控制的,但實際上“GLKView”類中完全不需要寫任何自己的代碼,因為,“GLKView”類中每幀觸發的兩個方法“update”和“glkView”,都轉交給“GLKViewController”代理執行了。

 

1、添加代理方法

在“ViewController.m”中添加兩個方法:

從零開始構建IOS的OpenGL應用(五) - mississi - mississi的博客

  

這兩個方法每幀都執行一次(循環執行),一般執行頻率與屏幕刷新率相同(但也可以更改)。

第一次循環時,先調用“glkView”再調用“update”。

一般,將場景數據變化放在“update”中,而渲染代碼則放在“glkView”中。

2、渲染場景

 

從零開始構建IOS的OpenGL應用(五) - mississi - mississi的博客

  

前兩行為渲染前的“清除”操作,清除顏色緩沖區和深度緩沖區中的內容,並且填充淡藍色背景(默認背景是黑色)。

“prepareToDraw”方法,是讓“效果Effect”針對當前“Context”的狀態進行一些配置,它始終把“GL_TEXTURE_PROGRAM”狀態定位到“Effect”對象的着色器上。此外,如果Effect使用了紋理,它也會修改“GL_TEXTURE_BINDING_2D”。

 

接下來,用“glDrawArrays”指令,讓OpenGL“畫出”兩個三角形(拼合為一個正方形)。OpenGL會自動從通用頂點屬性中取出這些數據、組裝、再用“Effect”內置的着色器渲染。

 

3、結果

從零開始構建IOS的OpenGL應用(五) - mississi - mississi的博客

 

渲染內容終於呈現了,藍色背景、還有一個綠色矩形(其實是兩個三角形)。綠色並非是此物體的本色,而受是綠色燈光影響。

PS:在前面的頂點數據定義中,期望得到一個正方形,但為什么顯示結果卻是一個矩形?

 

六、正確顯示正方形外觀

默認,“Effect”的投影矩陣是一個單位矩陣,它不做任何變換,將場景(-1,-1,-1)到(1,1,1)的立文體范圍的物體,投射到屏幕的X:-1,1,Y:-1,1。因此,當屏幕本身是非正方形時,正方形的物體將被拉伸,從而顯示為矩形。

實際上,默認的“Effect”模型視圖矩陣也是一個單位矩陣。

透視投影中的觀察點位於原點(0,0,0),並沿着Z軸的負方向進行觀察,就像是從屏幕內部看進去。

 

1、修改投影矩陣

為了正確顯示,需要修改投影矩陣。在“update”方法中添加下面的代碼:

從零開始構建IOS的OpenGL應用(六) - mississi - mississi的博客

 

首先計算出屏幕的縱橫比(aspect),然后縮放單位矩陣的Y軸,強制將Y軸的單位刻度與X軸保持一致。

 

2、渲染觀察效果

從零開始構建IOS的OpenGL應用(六) - mississi - mississi的博客

 

3、使用透視投影矩陣

把單位矩陣做拉伸,本質上仍然是一個正交投影。要模擬人眼觀察世界的效果,則必須使用透視投影。

把上面的代碼做一些修改:

從零開始構建IOS的OpenGL應用(六) - mississi - mississi的博客

 

使用GLKit自帶的方法創建出一個透視矩陣,視角、縱橫比、近平面、遠平面。

渲染效果如下:

從零開始構建IOS的OpenGL應用(六) - mississi - mississi的博客

 

4、修改模型視圖矩陣

上圖看起來感覺像一個正方形,但似乎左右兩邊沒顯示完整。

原因是,正方形與透視視點距離太近。

有兩個方法解決這個問題:一是修改原始的頂點數據(Z軸值),使之透視視點;二是通過所謂的“模型視圖矩陣”,將正方形“變換”到遠一點的位置。

添加以下代碼:

從零開始構建IOS的OpenGL應用(六) - mississi - mississi的博客

 這樣,同樣再次顯出一個精確的正方形。

 

七、使用紋理

1、准備紋理

在PS中剪切、調節紋理尺寸(512*512),並保存為Tulips.JPG。本例中使用的圖像是一幅黃色的郁金香。

然后在XCODE,導入進工程中。

從零開始構建IOS的OpenGL應用(七) - mississi - mississi的博客

 

2、使用GLKTextureLoader加載紋理

在“viewDidLoad”方法的后面,追加下列代碼:

從零開始構建IOS的OpenGL應用(七) - mississi - mississi的博客

 

首先用“NSBundle”找到資源“Tulips.jpg”的路徑,然后用“GLKTextureLoader”類方法同步加載這個紋理,也可以用它的實例方法異步進行加載。

默認,此圖片加載進TEXTURE0,如果需要加載進其他單元,需要先用指令“glActiveTexure(GL_TEXTUREn)”。——n為1-(CL_COMBINED_TEXTURE_IMAGE_UNITS-1)中的一個數值。

加載成功后,該紋理的信息都保存在“textureInfo”中,以后,直接使用此變量的相關屬性,就可以在OpenGL中應用這個紋理了。

 

3、將紋理綁定到Effect

接着,繼續添加后續代碼:

從零開始構建IOS的OpenGL應用(七) - mississi - mississi的博客

 

4、渲染

從零開始構建IOS的OpenGL應用(七) - mississi - mississi的博客

 

與意料中的結果似乎有差距,黃色的花瓣變成了綠色?圖像是上下顛倒的?

 

八、紋理細節調整

造成上面錯誤是原因是:

在最初構造Effect光照時,使用了綠色,所以整個紋理被“染”成為綠色。

圖像顛倒是因為紋理的坐標原點不在左下角。

1、修改光照顏色

從零開始構建IOS的OpenGL應用(八) - mississi - Mississippi的博客

 

2、將紋理坐標原點改為左下角

GLKit加載紋理,默認都是把坐標設置在“左上角”。然而,OpenGL的紋理貼圖坐標卻是在左下角,這樣剛好顛倒。

在加載紋理之前,添加一個“options”:

從零開始構建IOS的OpenGL應用(八) - mississi - Mississippi的博客

 

這個參數可以讓系統在加載紋理后,做一些基本的處理。如預乘Alpha、創建“Mipmaps”等。

 

3、渲染,一切正常

從零開始構建IOS的OpenGL應用(八) - mississi - Mississippi的博客
 
 

九、使用自定義的着色器

迄今,例中只是簡單地調用了“GLKit”內置的着色程序進行渲染。但是,在某些情況下,可能需要使用自己的特殊的着色器。

1、編寫着色程序

一個着色器由兩個部分構成(可以是兩個文件,也可以是硬編碼嵌在程序中的兩段代碼字串)。

它們分別是:頂點着色程序和片段着色程序。

創建兩個“Empty”文件,分別命名為“v.shader”和“f.shader”。

 

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客
  

然后,兩個文件分別寫入這些代碼:

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客

2、加載、存儲、編譯、附着、鏈接

在OpenGL中使用自定義着色器,過程比較繁瑣。

首先需要加載這個文件-->把它轉換為GLChar(UTF8編碼)-->保存進GUP內存-->編譯內存中的字串代碼-->附着給“program”對象。

上述過程要進行兩次(分別為頂點程序和片段程序)。

最后,將“program”鏈接到當前“Context”,這樣才能在OpenGL中發揮作用。

為了簡化代碼,可以寫成兩個方法,作為公共的加載方法使用:

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客
  

3、開始加載自定義的着色器

在“viewDidLoad”方法里追加以下代碼:

 

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客

如何判斷着色器是否能正常工作?可以用:

glGetProgramiv(_program, GL_LINK_STATUS, &params);

如果返回的“params”為1,則說明一切正常。

另外,上面代碼中“_program”為新添加進去的公共變量

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客
   

4、為着色器提供參數

頂點着色程序需要一個屬性參數:position(表示頂點的位置)

 

5、在“glkView”方法后追加

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客

渲染結果為:

從零開始構建IOS的OpenGL應用(九) - mississi - Mississippi的博客

屏幕上出現兩個矩形,有圖案的是用“effct”渲染的,上面紅色的是用“_program”自定義着色器渲染的。

十、增加着色器顯示紋理

上述着色器是一個超級簡單的着色器(幾乎沒實現什么功能,僅是簡單地着為紅色)。

下面逐漸增加它的功能。

 

1、修改着色器

給頂點着色器,增加紋理坐標屬性“TexCoord”和該坐標的輸出“coord”(此輸出將在片段着色器中使用)。

從零開始構建IOS的OpenGL應用(十) - mississi - Mississippi的博客

給片段着色器,增加紋理坐標輸入“coord”,以及統一的“sampler2D”變量。

從零開始構建IOS的OpenGL應用(十) - mississi - Mississippi的博客
 片段的顏色改為由“texture2D”函數計算出來,實際上就是按紋理坐標從紋理像素中取樣。

2、綁定着色器變量

要使着色器正常工作,必須提供它需要的參數內容。

在“viewDidload”方法后,添加“綁定”代碼:

從零開始構建IOS的OpenGL應用(十) - mississi - Mississippi的博客
  

第一部分是綁定“position”屬性到通用的的頂點屬性索引“0”上,綁定“texCoord”到通用的頂點屬性索引“3”上。(索引1是法線,2是頂點顏色)。

綁定后,必須調用“glLinkProgram”方法才能生效。

 

第二部分,綁定“統一的紋理sampler2D”變量,到紋理0號單元——在使用“GLKTextureLoader”加載紋理時,默認是激活了“0”號單元。當然,如果是激活其他單元(例如8),則這里就相應的改為8。

綁定之前,必須調用“glUseProgram”才起作用。

 

3、運行渲染

從零開始構建IOS的OpenGL應用(十) - mississi - Mississippi的博客

 

十一、着色器頂點變換矩陣

在上述着色器代碼中,是直接使用:

gl_Position = position;

也就是說,頂點位置沒有經過任何變換,直接使用它的原始數據(所以它的圖像也被顯示為一個矩形)。

 

1、引入變換矩陣

修改頂點着色器代碼:

從零開始構建IOS的OpenGL應用(十一) - mississi - Mississippi的博客

  

添加了一個統一的矩陣變量“modelViewProjectionMatrix”(模型、視圖、投影矩陣,是這三個變換矩陣合並后(乘法),得到一個單個的矩陣)。將來,要在主程序中將矩陣值傳入。

 

2、傳入矩陣值

在“update”方法中,追加下面的代碼:

從零開始構建IOS的OpenGL應用(十一) - mississi - Mississippi的博客

  

查詢到“modelViewProjectionMatrix”變量à計算合並矩陣à傳給着色器。

傳入着色器的值是modelViewProjectionMatrix.m,注意后面的“m”,它表示是一維數組形式的矩陣。

 

3、再次渲染

從零開始構建IOS的OpenGL應用(十一) - mississi - Mississippi的博客

 

因為,“Effect”的變換矩陣與“着色器”的渲染結果相同,所以,顯示為兩個完全重合的正方形。

 

4、偏離屏幕中心

為了更便於觀察,下面將“着色器”渲染的正方形偏離一下。

修改代碼:

 

從零開始構建IOS的OpenGL應用(十一) - mississi - Mississippi的博客

 

在合並矩陣之前,先把“modelViewMatrix”做一個平移(1.0,1.0,-1.0)。

結果為:

 

從零開始構建IOS的OpenGL應用(十一) - mississi - Mississippi的博客

 

注意到兩個圖像的顏色略有差別,這是因為“Effect”內置的着色器使用了光源。而自定義的着色器沒有光效代碼,它完全照搬了紋理的“原色”。另外,后面那個正方形變小了,是因為它更遠離了“相機”。

 

 

十二、動畫

所謂動畫,其實就是在“update”中有規律地修改一些Matrix參數,連續刷新時,即產生動畫的“錯覺”。

1、旋轉動畫

添加一些代碼,如下:

 

從零開始構建IOS的OpenGL應用(十二) - mississi - Mississippi的博客

  

首先要添加一個全局旋轉變量“_rotation”。

然后讓它每幀旋轉一點點,並以此修改“modelVireMatrix”矩陣。

 

2、渲染動畫效果

從零開始構建IOS的OpenGL應用(十二) - mississi - Mississippi的博客

 

結果是,正方形圍繞“自己”進行了旋轉。

如果希望它繞屏幕中心旋轉,怎么做?

 

3、理解矩陣堆棧

OpenGL的矩陣變換是放在一個矩陣堆棧中的(后進先出),代碼中矩陣變換的次序,決定了堆棧中矩陣的變換順序。所以,上述矩陣的變換實際上是倒過來進行的:先做平移,再做旋轉,這樣它就圍繞屏幕中心旋轉了。

把上面的代碼中“平移”和“旋轉”交換一下次序即可:

 

從零開始構建IOS的OpenGL應用(十二) - mississi - Mississippi的博客

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM