iOS 开发 OpenGL 新手入门
一、搭建開發(fā)環(huán)境
1、打開XCODE,新建一個工程
選擇:IOS-->ApplicationàSingle View Application模板。
取名為“HelloOpenGL”,勾選“UseStoryboards”,然后創(chuàng)建。
?
??
2、添加必要的框架
在“Build Phases”欄,添加進(jìn)三個框架:
3、修改viewController.h
添加“#import <GLKit/glkit.h>”,并將它修改為繼承“GLKViewController”。
?
??
4、修改“view”的類
雙擊“MainStoryboard.storyboard”展開,選擇“view”。
?
??
然后,在其“Identity Inspector”中,將它的類改為“GLKView”。
?
?至此,OpenGL環(huán)境已基本搭建出來了。運(yùn)行,應(yīng)該不會報錯,盡管它目前仍是黑屏。
?
二、開始堆代碼
基本上,所有的代碼都在“ViewController.m”中寫。
1、添加全局屬性聲明
?
當(dāng)然,還得在實(shí)現(xiàn)部分補(bǔ)足“@synthesize context;”和“@synthesize effect;”。
?
2、添加一組頂點(diǎn)數(shù)據(jù)
這是一個正方形頂點(diǎn)的數(shù)組,實(shí)際上它是由二個三角形接合而成的。
?每行頂點(diǎn)數(shù)據(jù)的排列含義是:
頂點(diǎn)X、頂點(diǎn)Y,頂點(diǎn)Z、法線X、法線Y、法線Z、紋理S、紋理T。
在后面解析此數(shù)組時,將參考此規(guī)則。
頂點(diǎn)位置用于確定在什么地方顯示,法線用于光照模型計算,紋理則用在貼圖中。
一般約定為“頂點(diǎn)以逆時針次序出現(xiàn)在屏幕上的面”為“正面”。
世界坐標(biāo)是OpenGL中用來描述場景的坐標(biāo),Z+軸垂直屏幕向外,X+從左到右,Y+軸從下到上,是右手笛卡爾坐標(biāo)系統(tǒng)。我們用這個坐標(biāo)系來描述物體及光源的位置。
?
三、初始化OpenGL環(huán)境
1、基本的初始化代碼
在“viewDidLoad”方法內(nèi),補(bǔ)充初始化代碼:
?
第一部分:使用“ES2”創(chuàng)建一個“EAGLContext”實(shí)例。
第二部分:將“view”的context設(shè)置為這個“EAGLContext”實(shí)例的引用。并且設(shè)置顏色格式和深度格式。
第三部分:將此“EAGLContext”實(shí)例設(shè)置為OpenGL的“當(dāng)前激活”的“Context”。這樣,以后所有“GL”的指令均作用在這個“Context”上。隨后,發(fā)送第一個“GL”指令:激活“深度檢測”。
第四部分:創(chuàng)建一個GLK內(nèi)置的“著色效果”,并給它提供一個光源,光的顏色為綠色。
?
2、運(yùn)行
現(xiàn)在應(yīng)該是粉紅色屏幕了(目前場景仍是空的),說明初始化過程沒問題。
?
四、將頂點(diǎn)數(shù)據(jù)寫入通用的頂點(diǎn)屬性存儲區(qū)
1、寫入過程
首先將數(shù)據(jù)保存進(jìn)GUP的一個緩沖區(qū)中,然后再按一定規(guī)則,將數(shù)據(jù)取出,復(fù)制到各個通用頂點(diǎn)屬性中。
注:如果頂點(diǎn)數(shù)據(jù)只有一種類型(如單純的位置坐標(biāo)),換言之,在讀數(shù)據(jù)時,不需要確定第一個數(shù)據(jù)的內(nèi)存位置(總是從0開始),則不必事先保存進(jìn)緩沖區(qū)。
?
2、頂點(diǎn)數(shù)組保存進(jìn)緩沖區(qū)
?
這幾行代碼表示的含義是:聲明一個緩沖區(qū)的標(biāo)識(GLuint類型)à讓OpenGL自動分配一個緩沖區(qū)并且返回這個標(biāo)識的值à綁定這個緩沖區(qū)到當(dāng)前“Context”à最后,將我們前面預(yù)先定義的頂點(diǎn)數(shù)據(jù)“squareVertexData”復(fù)制進(jìn)這個緩沖區(qū)中。
注:參數(shù)“GL_STATIC_DRAW”,它表示此緩沖區(qū)內(nèi)容只能被修改一次,但可以無限次讀取。
?
3、將緩沖區(qū)的數(shù)據(jù)復(fù)制進(jìn)通用頂點(diǎn)屬性中
??
首先,激活頂點(diǎn)屬性(默認(rèn)它的關(guān)閉的)。“GLKVertexAttribPosition”是頂點(diǎn)屬性集中“位置Position”屬性的索引。
頂點(diǎn)屬性集中包含五種屬性:位置、法線、顏色、紋理0,紋理1。
它們的索引值是0到4。
激活后,接下來使用“glVertexAttribPointer”方法填充數(shù)據(jù)。
參數(shù)含義分別為:
頂點(diǎn)屬性索引(這里是位置)、3個分量的矢量、類型是浮點(diǎn)(GL_FLOAT)、填充時不需要單位化(GL_FALSE)、在數(shù)據(jù)數(shù)組中每行的跨度是32個字節(jié)(4*8=32。從預(yù)定義的數(shù)組中可看出,每行有8個GL_FLOAT浮點(diǎn)值,而GL_FLOAT占4個字節(jié),因此每一行的跨度是4*8)。
最后一個參數(shù)是一個偏移量的指針,用來確定“第一個數(shù)據(jù)”將從內(nèi)存數(shù)據(jù)塊的什么地方開始。
?
4、繼續(xù)復(fù)制其他數(shù)據(jù)
在前面預(yù)定義的頂點(diǎn)數(shù)據(jù)數(shù)組中,還包含了法線和紋理坐標(biāo),所以參照上面的方法,將剩余的數(shù)據(jù)分別復(fù)制進(jìn)通用頂點(diǎn)屬性中。
?
原則上,必須先“激活”某個索引,才能將數(shù)據(jù)復(fù)制進(jìn)這個索引表示的內(nèi)存中。
因為紋理坐標(biāo)只有兩個(S和T),所以上面參數(shù)是“2”。
?
五、執(zhí)行渲染循環(huán)
萬事具備,現(xiàn)在可以讓OpenGL顯示一些東西了。
在GLKit框架中,盡管OpenGL的行為,是由“GLKViewController”和“GLKView”聯(lián)合控制的,但實(shí)際上“GLKView”類中完全不需要寫任何自己的代碼,因為,“GLKView”類中每幀觸發(fā)的兩個方法“update”和“glkView”,都轉(zhuǎn)交給“GLKViewController”代理執(zhí)行了。
?
1、添加代理方法
在“ViewController.m”中添加兩個方法:
??
這兩個方法每幀都執(zhí)行一次(循環(huán)執(zhí)行),一般執(zhí)行頻率與屏幕刷新率相同(但也可以更改)。
第一次循環(huán)時,先調(diào)用“glkView”再調(diào)用“update”。
一般,將場景數(shù)據(jù)變化放在“update”中,而渲染代碼則放在“glkView”中。
2、渲染場景
?
??
前兩行為渲染前的“清除”操作,清除顏色緩沖區(qū)和深度緩沖區(qū)中的內(nèi)容,并且填充淡藍(lán)色背景(默認(rèn)背景是黑色)。
“prepareToDraw”方法,是讓“效果Effect”針對當(dāng)前“Context”的狀態(tài)進(jìn)行一些配置,它始終把“GL_TEXTURE_PROGRAM”狀態(tài)定位到“Effect”對象的著色器上。此外,如果Effect使用了紋理,它也會修改“GL_TEXTURE_BINDING_2D”。
?
接下來,用“glDrawArrays”指令,讓OpenGL“畫出”兩個三角形(拼合為一個正方形)。OpenGL會自動從通用頂點(diǎn)屬性中取出這些數(shù)據(jù)、組裝、再用“Effect”內(nèi)置的著色器渲染。
?
3、結(jié)果
?
渲染內(nèi)容終于呈現(xiàn)了,藍(lán)色背景、還有一個綠色矩形(其實(shí)是兩個三角形)。綠色并非是此物體的本色,而受是綠色燈光影響。
PS:在前面的頂點(diǎn)數(shù)據(jù)定義中,期望得到一個正方形,但為什么顯示結(jié)果卻是一個矩形?
?
六、正確顯示正方形外觀
默認(rèn),“Effect”的投影矩陣是一個單位矩陣,它不做任何變換,將場景(-1,-1,-1)到(1,1,1)的立文體范圍的物體,投射到屏幕的X:-1,1,Y:-1,1。因此,當(dāng)屏幕本身是非正方形時,正方形的物體將被拉伸,從而顯示為矩形。
實(shí)際上,默認(rèn)的“Effect”模型視圖矩陣也是一個單位矩陣。
透視投影中的觀察點(diǎn)位于原點(diǎn)(0,0,0),并沿著Z軸的負(fù)方向進(jìn)行觀察,就像是從屏幕內(nèi)部看進(jìn)去。
?
1、修改投影矩陣
為了正確顯示,需要修改投影矩陣。在“update”方法中添加下面的代碼:
?
首先計算出屏幕的縱橫比(aspect),然后縮放單位矩陣的Y軸,強(qiáng)制將Y軸的單位刻度與X軸保持一致。
?
2、渲染觀察效果
?
3、使用透視投影矩陣
把單位矩陣做拉伸,本質(zhì)上仍然是一個正交投影。要模擬人眼觀察世界的效果,則必須使用透視投影。
把上面的代碼做一些修改:
?
使用GLKit自帶的方法創(chuàng)建出一個透視矩陣,視角、縱橫比、近平面、遠(yuǎn)平面。
渲染效果如下:
?
4、修改模型視圖矩陣
上圖看起來感覺像一個正方形,但似乎左右兩邊沒顯示完整。
原因是,正方形與透視視點(diǎn)距離太近。
有兩個方法解決這個問題:一是修改原始的頂點(diǎn)數(shù)據(jù)(Z軸值),使之透視視點(diǎn);二是通過所謂的“模型視圖矩陣”,將正方形“變換”到遠(yuǎn)一點(diǎn)的位置。
添加以下代碼:
?這樣,同樣再次顯出一個精確的正方形。
?
七、使用紋理
1、準(zhǔn)備紋理
在PS中剪切、調(diào)節(jié)紋理尺寸(512*512),并保存為Tulips.JPG。本例中使用的圖像是一幅黃色的郁金香。
然后在XCODE,導(dǎo)入進(jìn)工程中。
?
2、使用GLKTextureLoader加載紋理
在“viewDidLoad”方法的后面,追加下列代碼:
?
首先用“NSBundle”找到資源“Tulips.jpg”的路徑,然后用“GLKTextureLoader”類方法同步加載這個紋理,也可以用它的實(shí)例方法異步進(jìn)行加載。
默認(rèn),此圖片加載進(jìn)TEXTURE0,如果需要加載進(jìn)其他單元,需要先用指令“glActiveTexure(GL_TEXTUREn)”。——n為1-(CL_COMBINED_TEXTURE_IMAGE_UNITS-1)中的一個數(shù)值。
加載成功后,該紋理的信息都保存在“textureInfo”中,以后,直接使用此變量的相關(guān)屬性,就可以在OpenGL中應(yīng)用這個紋理了。
?
3、將紋理綁定到Effect
接著,繼續(xù)添加后續(xù)代碼:
?
4、渲染
?
與意料中的結(jié)果似乎有差距,黃色的花瓣變成了綠色?圖像是上下顛倒的?
?
八、紋理細(xì)節(jié)調(diào)整
造成上面錯誤是原因是:
在最初構(gòu)造Effect光照時,使用了綠色,所以整個紋理被“染”成為綠色。
圖像顛倒是因為紋理的坐標(biāo)原點(diǎn)不在左下角。
1、修改光照顏色
?
2、將紋理坐標(biāo)原點(diǎn)改為左下角
GLKit加載紋理,默認(rèn)都是把坐標(biāo)設(shè)置在“左上角”。然而,OpenGL的紋理貼圖坐標(biāo)卻是在左下角,這樣剛好顛倒。
在加載紋理之前,添加一個“options”:
?
這個參數(shù)可以讓系統(tǒng)在加載紋理后,做一些基本的處理。如預(yù)乘Alpha、創(chuàng)建“Mipmaps”等。
?
3、渲染,一切正常
九、使用自定義的著色器
迄今,例中只是簡單地調(diào)用了“GLKit”內(nèi)置的著色程序進(jìn)行渲染。但是,在某些情況下,可能需要使用自己的特殊的著色器。
1、編寫著色程序
一個著色器由兩個部分構(gòu)成(可以是兩個文件,也可以是硬編碼嵌在程序中的兩段代碼字串)。
它們分別是:頂點(diǎn)著色程序和片段著色程序。
創(chuàng)建兩個“Empty”文件,分別命名為“v.shader”和“f.shader”。
?
??然后,兩個文件分別寫入這些代碼:
2、加載、存儲、編譯、附著、鏈接
在OpenGL中使用自定義著色器,過程比較繁瑣。
首先需要加載這個文件-->把它轉(zhuǎn)換為GLChar(UTF8編碼)-->保存進(jìn)GUP內(nèi)存-->編譯內(nèi)存中的字串代碼-->附著給“program”對象。
上述過程要進(jìn)行兩次(分別為頂點(diǎn)程序和片段程序)。
最后,將“program”鏈接到當(dāng)前“Context”,這樣才能在OpenGL中發(fā)揮作用。
為了簡化代碼,可以寫成兩個方法,作為公共的加載方法使用:
??3、開始加載自定義的著色器
在“viewDidLoad”方法里追加以下代碼:
?
如何判斷著色器是否能正常工作?可以用:
glGetProgramiv(_program, GL_LINK_STATUS, ¶ms);
如果返回的“params”為1,則說明一切正常。
另外,上面代碼中“_program”為新添加進(jìn)去的公共變量
???4、為著色器提供參數(shù)
頂點(diǎn)著色程序需要一個屬性參數(shù):position(表示頂點(diǎn)的位置)
?
5、在“glkView”方法后追加
渲染結(jié)果為:
屏幕上出現(xiàn)兩個矩形,有圖案的是用“effct”渲染的,上面紅色的是用“_program”自定義著色器渲染的。
十、增加著色器顯示紋理
上述著色器是一個超級簡單的著色器(幾乎沒實(shí)現(xiàn)什么功能,僅是簡單地著為紅色)。
下面逐漸增加它的功能。
?
1、修改著色器
給頂點(diǎn)著色器,增加紋理坐標(biāo)屬性“TexCoord”和該坐標(biāo)的輸出“coord”(此輸出將在片段著色器中使用)。
給片段著色器,增加紋理坐標(biāo)輸入“coord”,以及統(tǒng)一的“sampler2D”變量。
?片段的顏色改為由“texture2D”函數(shù)計算出來,實(shí)際上就是按紋理坐標(biāo)從紋理像素中取樣。2、綁定著色器變量
要使著色器正常工作,必須提供它需要的參數(shù)內(nèi)容。
在“viewDidload”方法后,添加“綁定”代碼:
??第一部分是綁定“position”屬性到通用的的頂點(diǎn)屬性索引“0”上,綁定“texCoord”到通用的頂點(diǎn)屬性索引“3”上。(索引1是法線,2是頂點(diǎn)顏色)。
綁定后,必須調(diào)用“glLinkProgram”方法才能生效。
?
第二部分,綁定“統(tǒng)一的紋理sampler2D”變量,到紋理0號單元——在使用“GLKTextureLoader”加載紋理時,默認(rèn)是激活了“0”號單元。當(dāng)然,如果是激活其他單元(例如8),則這里就相應(yīng)的改為8。
綁定之前,必須調(diào)用“glUseProgram”才起作用。
?
3、運(yùn)行渲染
?
十一、著色器頂點(diǎn)變換矩陣
在上述著色器代碼中,是直接使用:
gl_Position = position;
也就是說,頂點(diǎn)位置沒有經(jīng)過任何變換,直接使用它的原始數(shù)據(jù)(所以它的圖像也被顯示為一個矩形)。
?
1、引入變換矩陣
修改頂點(diǎn)著色器代碼:
??
添加了一個統(tǒng)一的矩陣變量“modelViewProjectionMatrix”(模型、視圖、投影矩陣,是這三個變換矩陣合并后(乘法),得到一個單個的矩陣)。將來,要在主程序中將矩陣值傳入。
?
2、傳入矩陣值
在“update”方法中,追加下面的代碼:
??
查詢到“modelViewProjectionMatrix”變量à計算合并矩陣à傳給著色器。
傳入著色器的值是modelViewProjectionMatrix.m,注意后面的“m”,它表示是一維數(shù)組形式的矩陣。
?
3、再次渲染
?
因為,“Effect”的變換矩陣與“著色器”的渲染結(jié)果相同,所以,顯示為兩個完全重合的正方形。
?
4、偏離屏幕中心
為了更便于觀察,下面將“著色器”渲染的正方形偏離一下。
修改代碼:
?
?
在合并矩陣之前,先把“modelViewMatrix”做一個平移(1.0,1.0,-1.0)。
結(jié)果為:
?
?
注意到兩個圖像的顏色略有差別,這是因為“Effect”內(nèi)置的著色器使用了光源。而自定義的著色器沒有光效代碼,它完全照搬了紋理的“原色”。另外,后面那個正方形變小了,是因為它更遠(yuǎn)離了“相機(jī)”。
?
?
十二、動畫
所謂動畫,其實(shí)就是在“update”中有規(guī)律地修改一些Matrix參數(shù),連續(xù)刷新時,即產(chǎn)生動畫的“錯覺”。
1、旋轉(zhuǎn)動畫
添加一些代碼,如下:
?
??
首先要添加一個全局旋轉(zhuǎn)變量“_rotation”。
然后讓它每幀旋轉(zhuǎn)一點(diǎn)點(diǎn),并以此修改“modelVireMatrix”矩陣。
?
2、渲染動畫效果
?
結(jié)果是,正方形圍繞“自己”進(jìn)行了旋轉(zhuǎn)。
如果希望它繞屏幕中心旋轉(zhuǎn),怎么做?
?
3、理解矩陣堆棧
OpenGL的矩陣變換是放在一個矩陣堆棧中的(后進(jìn)先出),代碼中矩陣變換的次序,決定了堆棧中矩陣的變換順序。所以,上述矩陣的變換實(shí)際上是倒過來進(jìn)行的:先做平移,再做旋轉(zhuǎn),這樣它就圍繞屏幕中心旋轉(zhuǎn)了。
把上面的代碼中“平移”和“旋轉(zhuǎn)”交換一下次序即可:
?
(完)
轉(zhuǎn)載于:https://www.cnblogs.com/suoyin/p/7018991.html
總結(jié)
以上是生活随笔為你收集整理的iOS 开发 OpenGL 新手入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LeetCode-面试算法经典-Jav
- 下一篇: linux下confstr与uname函