原創(chuàng):知識(shí)探索型文章
創(chuàng)作不易读恃,請(qǐng)珍惜鸿吆,之后會(huì)持續(xù)更新囤采,不斷完善
個(gè)人比較喜歡做筆記和寫總結(jié),畢竟好記性不如爛筆頭哈哈惩淳,這些文章記錄了我的IOS成長(zhǎng)歷程蕉毯,希望能與大家一起進(jìn)步
溫馨提示:由于簡(jiǎn)書不支持目錄跳轉(zhuǎn),大家可通過(guò)command + F 輸入目錄標(biāo)題后迅速尋找到你所需要的內(nèi)容
目錄
- 一黎泣、OpenGL的知識(shí)點(diǎn)
- 1恕刘、OpenGL 專業(yè)名詞
- 2缤谎、OpenGL 坐標(biāo)系
- 3抒倚、著?器和圖片的渲染流程
- 4、搭建Open GL項(xiàng)目的運(yùn)行環(huán)境
- 二坷澡、使用OpenGL繪制圖形的流程
- 1托呕、繪制三角形
- 2、繪制正方形
- 三频敛、OpenGL的圖元類型
- 1项郊、各種需要的類
- 2、點(diǎn) GL_POINTS
- 3斟赚、條帶線 GL_LINE_STRIP
- 4着降、循環(huán)線 GL_LINE_LOOP
- 5、獨(dú)立三角形 GL_TRIANGLES
- 四拗军、使用存儲(chǔ)著色器
- 1任洞、著色器的使用
- 2、著色器種類
- 五发侵、OpenGL 渲染技巧
- 1交掏、繪制甜甜圈
- 2、正面&背面剔除
- 3刃鳄、深度測(cè)試
- 4盅弛、混合
- Demo
- 參考文獻(xiàn)
一、OpenGL的知識(shí)點(diǎn)
1叔锐、OpenGL 專業(yè)名詞
圖形API種類
- OpenGL (Open Graphics Library):是一個(gè)跨編程語(yǔ)言挪鹏、跨平臺(tái)的編程圖形程序接口
- OpenGL ES (OpenGL for Embedded Systems):是OpenGL三維圖形API的子集,針對(duì)手機(jī)愉烙、Pad和游戲主機(jī)等嵌入式設(shè)備而設(shè)計(jì)讨盒,去除了許多不必要和性能較低的APl接口
- Metal:Apple為游戲開發(fā)者推出了新的平臺(tái)技術(shù)Metal,該技術(shù)能夠?yàn)?D圖像提高10倍的渲染性能齿梁。Metal是Apple為了解決3D渲染而推出的框架
- DirectX:屬于Windows上一個(gè)多媒體處理APl催植,并不支持Windows以外的平臺(tái)
圖形API的用途
- 簡(jiǎn)單來(lái)說(shuō)就是實(shí)現(xiàn)圖形的底層渲染肮蛹,利用GPU芯?片來(lái)高效渲染圖形圖像,圖形API 是iOS開發(fā)者唯?接近GPU的?式
- 在游戲開發(fā)中创南,對(duì)于游戲場(chǎng)景/游戲人物的渲染
- 在音視頻開發(fā)中伦忠,對(duì)于視頻解碼后的數(shù)據(jù)渲染
- 在地圖引擎,對(duì)于地圖上的數(shù)據(jù)渲染
- 在動(dòng)畫中實(shí)現(xiàn)動(dòng)畫的繪制
- 在視頻處理中稿辙,對(duì)于視頻加上濾鏡效果
OpenGL 上下文(context)
在應(yīng)用程序調(diào)?任何OpenGL的指令之前昆码,需要安排?先創(chuàng)建?一個(gè)OpenGL的上下文。這個(gè)上下文是一個(gè)?常龐?大的狀態(tài)機(jī)邻储,保存了了OpenGL中的各種狀態(tài)赋咽,這也是OpenGL指令執(zhí)行的基礎(chǔ)。OpenGL的函數(shù)不管在哪個(gè)語(yǔ)?中吨娜,都是類似C語(yǔ)言?樣的面向過(guò)程的函數(shù)脓匿。
OpenGL 狀態(tài)機(jī)
- OpenGL可以記錄自己的狀態(tài)(如當(dāng)前所使用的顏色、是否開啟了顏色混合功能等)
- OpenGL可以接收輸入(當(dāng)調(diào)?用OpenGL函數(shù)的時(shí)候宦赠,實(shí)際上可以看成 OpenGL在接收我們的輸入)陪毡,如我們調(diào)?
glColor3f
,則OpenGL接收到這個(gè)輸入后會(huì)修改?己的“當(dāng)前顏色”這個(gè)狀態(tài) - OpenGL可以進(jìn)入停止?fàn)顟B(tài)勾扭,不再接收輸入毡琉。在程序退出前,OpenGL總會(huì)先停?工作的
渲染(Rendering)
將圖形/圖像數(shù)據(jù)轉(zhuǎn)換成3D空間圖像操作叫做渲染妙色。
頂點(diǎn)數(shù)組(VertexArray)和頂點(diǎn)緩沖區(qū)(VertexBuffer)
畫圖?般是先畫好圖像的骨架桅滋,然后再往?架?面填充顏色,這對(duì)于 OpenGL也是?樣的身辨。頂點(diǎn)數(shù)據(jù)就是要畫的圖像的骨架丐谋,和現(xiàn)實(shí)中不同的是,OpenGL中的圖像都是由圖元組成栅表。在OpenGLES中笋鄙,有3種類型的圖元:點(diǎn)、線怪瓶、三?形萧落。
那這些頂點(diǎn)數(shù)據(jù)最終是存儲(chǔ)在哪里的呢?開發(fā)者可以選擇設(shè)定函數(shù)指針洗贰,在調(diào)?繪制?法的時(shí)候找岖,直接由內(nèi)存?zhèn)魅腠旤c(diǎn)數(shù)據(jù),也就是說(shuō)這部分?jǐn)?shù)據(jù)之前是存儲(chǔ)在內(nèi)存當(dāng)中的敛滋,被稱為頂點(diǎn)數(shù)組许布。性能更高的做法是,提前分配?塊顯存绎晃,將頂點(diǎn)數(shù)據(jù)預(yù)先傳?到顯存當(dāng)中蜜唾。這部分的顯存杂曲,就被稱為頂點(diǎn)緩沖區(qū)。
管線
之所以稱之為管線是因?yàn)轱@卡在處理數(shù)據(jù)的時(shí)候是按照固定的順序來(lái)的袁余。就像?從?根管?的?端流到另一端擎勘,這個(gè)順序是不能打破的。
固定管線(存儲(chǔ)著色器)
在早期的OpenGL版本颖榜,它封裝了很多種著色器程序塊棚饵,內(nèi)置的一段包含了光照覆履、坐標(biāo)變換老充、裁剪等等諸多功能的固定shader程序幫助開發(fā)者完成圖形的渲染惋啃,開發(fā)者只需要傳入相應(yīng)的參數(shù)输涕,就能快速完成圖形的渲染。類似于iOS開發(fā)會(huì)封裝很多APL而我們只需要調(diào)用荐吵,就可以實(shí)現(xiàn)功能不需要關(guān)注底層實(shí)現(xiàn)原理锅棕。但是由于OpenGL的使用場(chǎng)景非常豐富巡语,固定管線或存儲(chǔ)著色器無(wú)法完成每一個(gè)業(yè)務(wù)缅疟,這時(shí)將需要部分開放成可編程分别。
著色器程序shader
將固定渲染管線架構(gòu)變?yōu)榱丝删幊啼秩竟芫€。因此存淫,OpenGL在實(shí)際調(diào)用繪制函數(shù)之前,還需要指定一個(gè)由shader
編譯成的著色器程序沼填。常 見(jiàn)的著色器主要有頂點(diǎn)著色器(VertexShader
) 桅咆,片段著色器(FragmentShader) 。
OpenGL在處理shader
時(shí)坞笙, 和其他編譯器一樣岩饼。通過(guò)編譯、 鏈接等步驟薛夜,生成了著色器程序(glProgram
) 籍茧,著色器程序同時(shí)包含了頂點(diǎn)著色器和片段著色器的運(yùn)算邏輯。在OpenGL進(jìn)行繪制的時(shí)候梯澜,首先由頂點(diǎn)著色器對(duì)傳入的頂點(diǎn)數(shù)據(jù)進(jìn)行運(yùn)算寞冯。再通過(guò)圖元裝配,將頂點(diǎn)轉(zhuǎn)換為圖元晚伙。然后進(jìn)行光柵化吮龄,將圖元這種矢量圖形,轉(zhuǎn)換為柵格化數(shù)據(jù)咆疗。最后漓帚,將柵格化數(shù)據(jù)傳入片段著色器中進(jìn)行運(yùn)算。片段著色器會(huì)對(duì)柵格化數(shù)據(jù)中的每一個(gè)像素進(jìn)行運(yùn)算午磁,并決定像素的顏色
頂點(diǎn)著色器(VertexShader)
一般用來(lái)處理圖形每個(gè)頂點(diǎn)變換[旋轉(zhuǎn)/平移/投影等]尝抖。頂點(diǎn)著色器是OpenGL中用于計(jì)算頂點(diǎn)屬性的程序毡们。頂點(diǎn)著色器是逐頂點(diǎn)運(yùn)算的程序,也就是說(shuō)每個(gè)頂點(diǎn)數(shù)據(jù)都會(huì)執(zhí)行一次頂點(diǎn)著色器昧辽,當(dāng)然這是并行的漏隐,并且頂點(diǎn)著色器運(yùn)算過(guò)程中無(wú)法訪問(wèn)其他頂點(diǎn)的數(shù)據(jù)。
片元著色器程序(FragmentShader)
一般用來(lái)處理圖形中每個(gè)像素點(diǎn)顏色計(jì)算和填充奴迅。片段著色器是OpenGL中用于計(jì)算片段(像素)顏色的程序青责。片段著色器是逐像素運(yùn)算的程序,也就是說(shuō)每個(gè)像素都會(huì)執(zhí)行一次片段著色器取具,當(dāng)然也是并行的脖隶。
OpenGL著色語(yǔ)言(OpenGL Shading Language)
用來(lái)在OpenGL中著色編程的語(yǔ)言,也即開發(fā)人員寫的短小的自定義程序暇检,他們是在圖形卡的GPU (Graphic Processor Unit圖形處理單元)上執(zhí)行的产阱, 代替了固定的渲染管線的一部分,使渲染管線中不同層次具有可編程性块仆。比如:視圖轉(zhuǎn)換构蹬、投影轉(zhuǎn)換等。
光柵化Rasterization
光柵化就是把頂點(diǎn)數(shù)據(jù)轉(zhuǎn)換為片元的過(guò)程悔据,將圖轉(zhuǎn)化為一個(gè)個(gè)柵格組成的圖象庄敛。片元中的每個(gè)元素對(duì)應(yīng)于幀緩沖區(qū)中的一個(gè)像素。光柵化其實(shí)是一種將幾何圖元變?yōu)槎S圖像的過(guò)程科汗。第一部分工作:決定窗口坐標(biāo)中的哪些整型柵格區(qū)域被基本圖元占用藻烤。第二部分工作:分配一個(gè)顏色值和一個(gè)深度值到各個(gè)區(qū)域。
紋理
物理世界中头滔,視域內(nèi)的顏色會(huì)發(fā)生快速的變化怖亭。你可以看到很多物體表面都會(huì)呈現(xiàn)出豐富的顏色,并且在狹小的面積上產(chǎn)生多彩的變化坤检。要捕捉細(xì)節(jié)如此豐富的色彩變化是非常辛苦和縝密的工作(你需要有效地辨別每個(gè)線性色彩變化區(qū)域中的每個(gè)三角形)兴猩。如果能找到一張圖片,然后把它“粘”到物體表面上早歇,就像貼墻紙一樣倾芝,那就簡(jiǎn)單多了。這就是 紋理映射(texture mapping
)缺前。在渲染圖形時(shí)為了使得場(chǎng)景更加逼真需要填充圖片蛀醉,這里使用的圖片就是常說(shuō)的紋理。
混合(Blending)
在測(cè)試階段之后衅码,如果像素依然沒(méi)有被剔除拯刁,那么像素的顏色將會(huì)和幀緩沖區(qū)中顏色附著上的顏色進(jìn)行混合,混合的算法可以通過(guò)0penGL的函數(shù)進(jìn)行指定逝段。但是OpenGL提供的混合算法是有限的垛玻,如果需要更加復(fù)雜的混合算法割捅,一般可以通過(guò)像素(片段)著色器進(jìn)行實(shí)現(xiàn),當(dāng)然性能會(huì)比原生的混合算法差一些帚桩。
變換矩陣Transformation
圖形想發(fā)生平移亿驾、縮放、旋轉(zhuǎn)變換就需要使用變換矩陣账嚎。
投影矩陣Projection
用于將3D坐標(biāo)轉(zhuǎn)換為二維屏幕坐標(biāo)莫瞬。
渲染上屏/交換緩沖區(qū)SwapBuffer
渲染緩沖區(qū)一般映射的是系統(tǒng)的窗口。如果將圖像直接渲染到窗口對(duì)應(yīng)的渲染緩沖區(qū)郭蕉,則可以將圖像顯示到屏幕上疼邀。但是如果每個(gè)窗口只有一個(gè)緩沖區(qū),那么在繪制過(guò)程中屏幕進(jìn)行了刷新召锈,窗口可能顯示出不完整的圖像旁振。為了解決這個(gè)問(wèn)題,常規(guī)的OpenGL程序至少都會(huì)有兩個(gè)緩沖區(qū)涨岁。顯示在屏幕上的稱為屏幕緩沖區(qū)拐袜,沒(méi)有顯示的稱為離屏緩沖區(qū)。在一個(gè)緩沖區(qū)渲染完成之后梢薪,通過(guò)將屏幕緩沖區(qū)和離屏緩沖區(qū)交換蹬铺,實(shí)現(xiàn)圖像在屏幕上的顯示。
由于顯示器的刷新一般是逐行進(jìn)行的沮尿, 因此為了防止交換緩沖區(qū)的時(shí)候屏幕上下區(qū)域的圖像分屬于兩個(gè)不同的幀丛塌,因此交換一般會(huì)等待顯示器刷新完成的信號(hào),在顯示器兩次刷新的間隔中進(jìn)行交換畜疾,這個(gè)信號(hào)就被稱為垂直同步信號(hào),這個(gè)技術(shù)被稱為垂直同步印衔。
使用了雙緩沖區(qū)和垂直同步技術(shù)之后啡捶,由于總是要等待緩沖區(qū)交換之后再進(jìn)行下一幀的渲染,使得幀率無(wú)法完全達(dá)到硬件允許的最高水平奸焙。為了解決這個(gè)問(wèn)題瞎暑,引入了三緩沖區(qū)技術(shù),在等待垂直同步時(shí)与帆,來(lái)回交替渲染兩個(gè)離屏的緩沖區(qū)了赌,而垂直同步發(fā)生時(shí),屏幕緩沖區(qū)和最近渲染完成的離屏緩沖區(qū)交換玄糟,實(shí)現(xiàn)充分利用硬件性能的目的勿她。
2、OpenGL 坐標(biāo)系
a阵翎、坐標(biāo)系轉(zhuǎn)化
- 物體空間(Object Space) / 局部空間(Local Space)
- 世界空間(World Space)
- 觀察空間(View Space)
- 裁剪空間(Clip Space)
- 屏幕空間(Screen Space)
OpenGL希望每次頂點(diǎn)著色后逢并,我們的可見(jiàn)頂點(diǎn)都為標(biāo)準(zhǔn)化設(shè)備坐標(biāo)之剧,也就是說(shuō)每個(gè)頂點(diǎn)的x,y,z
都應(yīng)該在-1到1之間,超出這個(gè)范圍的頂點(diǎn)將是不可見(jiàn)的砍聊。通常情況下我們會(huì)自己設(shè)定一個(gè)坐標(biāo)范圍背稼,之后再在頂點(diǎn)著色器中將這些坐標(biāo)變換為設(shè)備坐標(biāo),然后這些標(biāo)化設(shè)備坐標(biāo)傳入光柵器(Rasterizer
) 將它們變換為屏幕上的二維坐標(biāo)和像素玻蝌。將坐標(biāo)變換為標(biāo)準(zhǔn)化設(shè)備坐標(biāo)蟹肘,接著再轉(zhuǎn)化為屏幕坐標(biāo)的過(guò)程通常是分步進(jìn)行的,也就是類似于流水線那樣子俯树。
在流水線中帘腹,物體的頂點(diǎn)在最終轉(zhuǎn)化為屏幕坐標(biāo)之前還會(huì)被變換到多個(gè)坐標(biāo)系統(tǒng)(Coordinate System
)。將物體的坐標(biāo)變換到幾個(gè)過(guò)渡坐標(biāo)系(Intermediate Coordinate System
)的優(yōu)點(diǎn)在于聘萨,在這些特定的坐標(biāo)系統(tǒng)中竹椒,一些操作或運(yùn)算更加方便和容易,這一點(diǎn)很快就會(huì)變得很明顯米辐。
一個(gè)頂點(diǎn)在最終被轉(zhuǎn)化為片段之前需要經(jīng)歷不同狀態(tài)胸完。為了將坐標(biāo)從一個(gè)坐標(biāo)系變換到另一個(gè)坐標(biāo)系,我們需要用到幾個(gè)變換矩陣翘贮,最重要的幾個(gè)分別是模型(Model
)赊窥、觀察(View
)、 投影(Projection
)三個(gè)矩陣 狸页。物體頂點(diǎn)的起始坐標(biāo)在局部空間(Local Space
) 锨能,稱它為局部坐標(biāo)(Local Coordinate
),它在之后會(huì)變成世界坐標(biāo)(world Coordinate
) 芍耘、觀測(cè)坐標(biāo)(View Coordinate
) 址遇、裁剪坐標(biāo)(Clip Coordinate
) 、并最后以屏幕坐標(biāo)(Screen Corrdinate
)的形式結(jié)束斋竞。
b倔约、在3D圖形學(xué)中常用的坐標(biāo)系
世界坐標(biāo)系
世界坐標(biāo)系是系統(tǒng)的絕對(duì)坐標(biāo)系,在沒(méi)有建立用戶坐標(biāo)系之前畫面上所有的點(diǎn)的坐標(biāo)都可以通過(guò)該坐標(biāo)系的原點(diǎn)來(lái)確定各自的位置坝初,世界坐標(biāo)系始終是固定不變的浸剩。
物體坐標(biāo)系
每個(gè)物體都有他們獨(dú)立的坐標(biāo)系,當(dāng)物理移動(dòng)或者改變方向時(shí)鳄袍,該物體相關(guān)聯(lián)的坐標(biāo)系將隨之移動(dòng)或改變方向绢要。物體坐標(biāo)系是以物體本身而言,比如拗小,我先向你發(fā)指令重罪,“向前走一步”,但我并不知道你會(huì)往哪個(gè)絕對(duì)的方向移動(dòng)。比如說(shuō)蛆封,當(dāng)你開車時(shí)唇礁,有人會(huì)說(shuō)向左轉(zhuǎn),有人說(shuō)向東惨篱。但是盏筐,向左轉(zhuǎn)是物體坐標(biāo)系的概念,而向東則是世界坐標(biāo)系中的概念砸讳。
攝像機(jī)(照相機(jī))坐標(biāo)系
照相機(jī)坐標(biāo)系是和觀察者密切相關(guān)的坐標(biāo)系琢融。照相機(jī)坐標(biāo)系和屏幕坐標(biāo)系相似,差別在于照相機(jī)坐標(biāo)系處于3D空間中簿寂,而屏幕坐標(biāo)系在2D平面里漾抬。
慣性坐標(biāo)系
指的是世界坐標(biāo)系到物體坐標(biāo)系的"中途"。慣性坐標(biāo)系的原點(diǎn)和物體坐標(biāo)原點(diǎn)重合常遂,但慣性坐標(biāo)系的軸平行于世界坐標(biāo)系的軸纳令。為什么要引入慣性坐標(biāo)系?因?yàn)槲矬w坐標(biāo)系轉(zhuǎn)換到慣性坐標(biāo)系只需要旋轉(zhuǎn)克胳,從慣性坐標(biāo)系轉(zhuǎn)換到世界坐標(biāo)系只需要平移平绩。
c、坐標(biāo)變換的全局圖
OpenGL最終的渲染設(shè)備是2D的漠另,我們需要將3D表示的場(chǎng)景轉(zhuǎn)換為最終的2D形式捏雌,前面使用模型變換和視變換將物體坐標(biāo)轉(zhuǎn)換到照相機(jī)坐標(biāo)系后,需要進(jìn)行投影變換笆搓,將坐標(biāo)從相機(jī)——》裁剪坐標(biāo)系性湿,經(jīng)過(guò)透視除法后,變換到規(guī)范化設(shè)備坐標(biāo)系(NDC)满败,最后進(jìn)行視口變換后肤频,3D坐標(biāo)才變換到屏幕上的2D坐標(biāo)。
OpenGL只定義 了裁剪坐標(biāo)系算墨、規(guī)范化設(shè)備坐標(biāo)系和屏幕坐標(biāo)系着裹,而局部坐標(biāo)系(模型坐標(biāo)系)、世界坐標(biāo)系和照相機(jī)坐標(biāo)系都是為了方便用戶設(shè)計(jì)而自定義的坐標(biāo)系米同。
圖中左邊的過(guò)程包括模型變換、視變換摔竿,投影變換面粮,這些變換可以由用戶根據(jù)需要自行指定,這些內(nèi)容在頂點(diǎn)著色器中完成继低。圖中右邊的兩個(gè)步驟熬苍,包括透視除法、視口變換,這兩個(gè)步驟是OpenGL自動(dòng)執(zhí)行的柴底,在頂點(diǎn)著色器處理后的階段完成婿脸。
OpenGL會(huì)對(duì)裁剪坐標(biāo)執(zhí)行透視除法從而將它們變換到標(biāo)準(zhǔn)化設(shè)備坐標(biāo)。OpenGL使用glViewPort
內(nèi)部的參數(shù)來(lái)將標(biāo)準(zhǔn)化設(shè)備坐標(biāo)映射到屏幕坐標(biāo)柄驻,每個(gè)坐標(biāo)都關(guān)聯(lián)了一個(gè)屏幕上的點(diǎn)狐树,這個(gè)過(guò)程稱為視口變換。
d鸿脓、模型變換與視變換
局部坐標(biāo)系(模型坐標(biāo)系)是為了方便構(gòu)造模型而設(shè)立的坐標(biāo)系抑钟,建立模型時(shí)我們無(wú)需關(guān)心最終對(duì)象顯示在屏幕哪個(gè)位置。模型變換的主要目的是通過(guò)變換使得用頂點(diǎn)屬性定義或者3d建模軟件構(gòu)造的模型野哭,能夠按照需要在塔,通過(guò)縮小、平移等操作放置到場(chǎng)景中合適的位置拨黔。通過(guò)模型變換后蛔溃,物體放置在一個(gè)全局的世界坐標(biāo)系中,世界坐標(biāo)系是所有物體交互的一個(gè)公共坐標(biāo)系篱蝇。
視變換是為了方便觀察場(chǎng)景中物體而設(shè)立的坐標(biāo)系贺待,在這個(gè)坐標(biāo)系中相機(jī)是個(gè)假想的概念,是為了便于計(jì)算而引入的态兴。相機(jī)坐標(biāo)系中的坐標(biāo)狠持,就是從相機(jī)的角度來(lái)解釋世界坐標(biāo)系中位置。OpenGL中相機(jī)始終位于原點(diǎn)瞻润,指向-Z軸喘垂,而以相反的方式來(lái)調(diào)整場(chǎng)景中物體,從而達(dá)到相同的觀察效果绍撞。例如要觀察-Z軸方向的一個(gè)立方體的右側(cè)面正勒,既可以讓立方體繞著+Y軸旋轉(zhuǎn)-90度,也可以讓立方體保持不動(dòng)傻铣,相機(jī)繞著+Y軸旋轉(zhuǎn)+90度章贞。
e、投影
3非洲、著?器和圖片的渲染流程
a鸭限、圖片顯示到屏幕上是CPU與GPU的協(xié)作完成
-
CPU:計(jì)算視圖
frame
,圖片解碼两踏,將需要繪制的紋理圖片通過(guò)數(shù)據(jù)總線交給GPU
- GPU:紋理混合败京,頂點(diǎn)變換與計(jì)算,像素點(diǎn)的填充計(jì)算梦染,渲染到幀緩沖區(qū)
-
時(shí)鐘信號(hào):垂直同步信號(hào)
V-Sync
/ 水平同步信號(hào)H-Sync
- iOS設(shè)備雙緩沖機(jī)制:顯示系統(tǒng)通常會(huì)引入兩個(gè)幀緩沖區(qū)赡麦,雙緩沖機(jī)制
b朴皆、CPU中對(duì)圖片進(jìn)行解壓縮操作
假設(shè)我們使用 +imageWithContentsOfFile:
方法從磁盤中加載一張圖片,這個(gè)時(shí)候的圖片并沒(méi)有解壓縮泛粹。然后將生成的 UIImage
賦值給 UIImageView
遂铡。接著一個(gè)隱式的CATransaction
捕獲到了UIImageView
圖層樹的變化。在主線程的下一個(gè) runloop
到來(lái)時(shí)晶姊,Core Animation
提交了這個(gè)隱式的 transaction
扒接,這個(gè)過(guò)程可能會(huì)對(duì)圖片進(jìn)行 copy
操作,這個(gè) copy
操作可能會(huì)涉及以下部分或全部步驟:
- 分配內(nèi)存緩沖區(qū)用于管理文件 IO 和解壓縮操作
- 將文件數(shù)據(jù)從磁盤讀到內(nèi)存中
- 將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式帽借,這是一個(gè)非常耗時(shí)的 CPU 操作
- 最后
Core Animation
中CALayer
使用未壓縮的位圖數(shù)據(jù)渲染UIImageView
的圖層 - CPU計(jì)算好圖片的
Frame
珠增,對(duì)圖片解壓之后就會(huì)交給GPU來(lái)做圖片渲染
我們提到了圖片的解壓縮是一個(gè)非常耗時(shí)的 CPU 操作,并且它默認(rèn)是在主線程中執(zhí)行的砍艾。那么當(dāng)需要加載的圖片比較多時(shí)蒂教,就會(huì)對(duì)我們應(yīng)用的響應(yīng)性造成嚴(yán)重的影響,尤其是在快速滑動(dòng)的列表上脆荷,這個(gè)問(wèn)題會(huì)表現(xiàn)得更加突出凝垛。
既然圖片的解壓縮需要消耗大量的 CPU 時(shí)間,那么我們?yōu)槭裁催€要對(duì)圖片進(jìn)行解壓縮呢蜓谋?是否可以不經(jīng)過(guò)解壓縮梦皮,而直接將圖片顯示到屏幕上呢?答案是否定的桃焕。要想弄明白這個(gè)問(wèn)題剑肯,我們首先需要知道什么是位圖。其實(shí)观堂,位圖就是一個(gè)像素?cái)?shù)組让网,數(shù)組中的每個(gè)像素就代表著圖片中的一個(gè)點(diǎn)。我們?cè)趹?yīng)用中經(jīng)常用到的 JPEG 和 PNG 圖片就是位圖师痕。不管是 JPEG 還是 PNG 圖片溃睹,都是一種壓縮的位圖圖形格式。只不過(guò) PNG 圖片是無(wú)損壓縮胰坟,并且支持 alpha 通道因篇,而 JPEG 圖片則是有損壓縮,可以指定 0-100% 的壓縮比笔横。因此竞滓,在將磁盤中的圖片渲染到屏幕之前,必須先要得到圖片的原始像素?cái)?shù)據(jù)吹缔,才能執(zhí)行后續(xù)的繪制操作虽界,這就是為什么需要對(duì)圖片解壓縮的原因。
既然圖片的解壓縮不可避免涛菠,而我們也不想讓它在主線程執(zhí)行,影響我們應(yīng)用的響應(yīng)性,那么是否有比較好的解決方案呢俗冻?我們前面已經(jīng)提到了礁叔,當(dāng)未解壓縮的圖片將要渲染到屏幕時(shí),系統(tǒng)會(huì)在主線程對(duì)圖片進(jìn)行解壓縮迄薄,而如果圖片已經(jīng)解壓縮了琅关,系統(tǒng)就不會(huì)再對(duì)圖片進(jìn)行解壓縮。因此讥蔽,也就有了業(yè)內(nèi)的解決方案涣易,在子線程提前對(duì)圖片進(jìn)行強(qiáng)制解壓縮。而強(qiáng)制解壓縮的原理就是對(duì)圖片進(jìn)行重新繪制冶伞,得到一張新的解壓縮后的位圖新症。其中,用到的最核心的函數(shù)是 CGBitmapContextCreate
响禽。
YYImage\SDWebImage
開源框架實(shí)現(xiàn)解壓縮的方法接受一個(gè)原始的位圖參數(shù) imageRef
徒爹,最終返回一個(gè)新的解壓縮后的位圖 newImage
,中間主要經(jīng)過(guò)了以下三個(gè)步驟:
- 使用
CGBitmapContextCreate
函數(shù)創(chuàng)建一個(gè)位圖上下文 - 使用
CGContextDrawImage
函數(shù)將原始位圖繪制到上下文中 - 使用
CGBitmapContextCreateImage
函數(shù)創(chuàng)建一張新的解壓縮后的位圖
c芋类、圖片渲染到屏幕的過(guò)程
- GPU獲取獲取圖片的坐標(biāo)
- 將坐標(biāo)交給頂點(diǎn)著色器(頂點(diǎn)計(jì)算)
- 將圖片光柵化(獲取圖片對(duì)應(yīng)屏幕上的像素點(diǎn))
- 片元著色器計(jì)算(計(jì)算每個(gè)像素點(diǎn)的最終顯示的顏色值)
- 從幀緩存區(qū)中渲染到屏幕上
d隆嗅、總的流程
讀取文件->計(jì)算Frame->圖片解碼->解碼后紋理圖片位圖數(shù)據(jù)通過(guò)數(shù)據(jù)總線交給GPU->GPU獲取圖片F(xiàn)rame->頂點(diǎn)變換計(jì)算->光柵化->根據(jù)紋理坐標(biāo)獲取每個(gè)像素點(diǎn)的顏色值(如果出現(xiàn)透明值需要將每個(gè)像素點(diǎn)的顏色*透明度值)->渲染到幀緩存區(qū)->渲染到屏幕
客戶端-服務(wù)器
管線上半部分是客戶端,下半部分是服務(wù)器侯繁。就 OpenGL 而言胖喳,客戶端是存儲(chǔ)在 CPU 存儲(chǔ)器中的溯饵,驅(qū)動(dòng)程序?qū)秩久钆c數(shù)據(jù)組合起來(lái)發(fā)給服務(wù)器執(zhí)行廓潜。服務(wù)器和客戶端在功能上是異步的』啵客戶端不斷的將數(shù)據(jù)和命令組合在一起送入緩沖區(qū)坝锰,緩沖區(qū)再發(fā)送到服務(wù)器執(zhí)行粹懒。
著色器
上圖中最大的框代表的是頂點(diǎn)著色器 和片元著色器。著色器是使用GLSL編寫的程序顷级。頂點(diǎn)著色器處理從客戶端輸入的數(shù)據(jù)凫乖,用數(shù)學(xué)運(yùn)算來(lái)計(jì)算光照效果、位移弓颈、顏色值等帽芽。有幾個(gè)頂點(diǎn),頂點(diǎn)著色器就要執(zhí)行幾次翔冀。
上圖中的圖元組合(Primitive Assembly)框圖意在說(shuō)明3個(gè)頂點(diǎn)已經(jīng)組合在了一起导街。
片元著色器來(lái)計(jì)算片元的最終顏色和它的深度值。在這里我們會(huì)使用紋理映射的方式纤子,對(duì)頂點(diǎn)處理階段所計(jì)算的顏色值進(jìn)行補(bǔ)充搬瑰。
頂點(diǎn)的著色器和片元著色器之間的區(qū)別在于頂點(diǎn)著色器決定了一個(gè)圖元應(yīng)該位于屏幕的什么位置款票,而片元著色器使用這些信息來(lái)決定某個(gè)片元的顏色應(yīng)該是什么。
Uniform值
通過(guò)設(shè)置 Uniform 變量就緊接著發(fā)送一個(gè)圖元批次處理命令泽论,設(shè)置一個(gè)應(yīng)用于整個(gè)表?的單個(gè)顏色值艾少。
4、搭建Open GL項(xiàng)目的運(yùn)行環(huán)境
? Xcode -> macOS -> APP
? 添加OpenGl.framework 和 GLUT.framework 兩個(gè)系統(tǒng)庫(kù)
? 在Bulid Settings的Header Search path中添加 CLTool.h 和 glew.h 的生成路徑
? 將libGLTools.a 直接拖到工程的Frameworks 里面翼悴,再刪除掉 AppDelegate文件 缚够、main.m文件、ViewController文件鹦赎,創(chuàng)建main.cpp文件
? 修改main.cpp文件如下所示
代碼見(jiàn)于繪制三角形的Demo
? 編譯報(bào)錯(cuò)后谍椅,將系統(tǒng)<>引入修改為普通引入“”
? Demo的運(yùn)行效果
二、使用OpenGL繪制圖形的流程
1古话、繪制三角形
a雏吭、導(dǎo)入依賴庫(kù)和創(chuàng)建成員變量
引入了GLTool
著色器管理器類(shader Mananger
)。沒(méi)有著色器煞额,我們就不能在OpenGL
(核心框架)進(jìn)行著色思恐。
#include "GLShaderManager.h"
GLTool.h
頭文件包含了大部分GLTool
中類似C語(yǔ)言的獨(dú)立函數(shù)。
#include "GLTools.h"
#include <GLUT/GLUT.h>
定義一個(gè)著色管理器
GLShaderManager shaderManager;
簡(jiǎn)單的批次容器膊毁,是GLTools
的一個(gè)簡(jiǎn)單的容器類
GLBatch triangleBatch;
b胀莹、程序入口函數(shù)
傳入命令參數(shù)初始化GLUT
庫(kù)
int main(int argc,char* argv[])
{
// 設(shè)置當(dāng)前工作目錄,針對(duì)MAC OS X
gltSetWorkingDirectory(argv[0]);
// 傳入命令參數(shù)初始化GLUT庫(kù)
glutInit(&argc, argv);
......
// 類似于iOS runloop 運(yùn)?循環(huán)
glutMainLoop();
return 0;
}
初始化雙緩沖窗口婚温,其中標(biāo)志GLUT_DOUBLE
描焰、GLUT_RGBA
、GLUT_DEPTH
栅螟、GLUT_STENCIL
分別指雙緩沖窗口荆秦、RGBA
顏色模式、深度測(cè)試力图、模板緩沖區(qū)步绸。雙緩存窗口 GLUT_DOUBLE
指繪圖命令實(shí)際上是離屏緩存區(qū)執(zhí)行的,然后迅速轉(zhuǎn)換成窗口視圖吃媒,這種方式經(jīng)常用來(lái)生成動(dòng)畫效果瓤介。
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
設(shè)置GLUT
窗口大小
glutInitWindowSize(800,600);
設(shè)置GLUT
窗口標(biāo)題
glutCreateWindow("Triangle");
GLUT
內(nèi)部運(yùn)行一個(gè)本地消息循環(huán),攔截適當(dāng)?shù)南⒆改牵缓笳{(diào)用我們注冊(cè)的回調(diào)函數(shù)
// 注冊(cè)窗口改變大小的重塑函數(shù)
glutReshapeFunc(ChangeSize);
// 注冊(cè)顯示函數(shù)
glutDisplayFunc(RenderScene);
初始化一個(gè)GLEW
庫(kù)刑桑,確保OpenGL API
對(duì)程序完全可用
GLenum err = glewInit();
在試圖做任何渲染之前,要檢查驅(qū)動(dòng)程序的初始化中沒(méi)有出現(xiàn)任何問(wèn)題
if(GLEW_OK != err)
{
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
設(shè)置用于渲染的數(shù)據(jù)
SetupRC();
c募舟、設(shè)置用于渲染的數(shù)據(jù)
設(shè)置清屏顏色(背景顏色)
void SetupRC()
{
// 設(shè)置清屏顏色(背景顏色)
glClearColor(0.0f,0.0f,1.0f,1.0f);
......
}
沒(méi)有著色器祠斧,在OpenGL 核心框架中是無(wú)法進(jìn)行任何渲染的,所以需要初始化一個(gè)渲染管理器拱礁。這里采用固管線渲染琢锋,后面會(huì)學(xué)著用OpenGL著色語(yǔ)言來(lái)寫著色器辕漂。
shaderManager.InitializeStockShaders();
設(shè)置三角形,其中數(shù)組vVert
包含3個(gè)頂點(diǎn)的x,y,z
GLfloat vVerts[] =
{
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,0.5f,0.0f,
};
批次處理將三角形數(shù)據(jù)傳遞到著色器
triangleBatch.Begin(GL_TRIANGLES,3);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
d吩蔑、開始渲染
清除一個(gè)或一組特定的緩沖區(qū)
- GL_COLOR_BUFFER_BIT:指示當(dāng)前激活的用來(lái)進(jìn)行顏色寫入緩沖區(qū)
- GL_DEPTH_BUFFER_BIT:指示深度緩存區(qū)
- GL_STENCIL_BUFFER_BIT:指示模板緩沖區(qū)
void RenderScene(void)
{
// 清除一個(gè)或一組特定的緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
.....
}
設(shè)置一組浮點(diǎn)數(shù)來(lái)表示紅色
GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
傳遞到存儲(chǔ)著色器钮热,即GLT_SHADER_IDENTITY
代表著單位(Identity
)色器,這個(gè)著色器只是使用指定顏色以默認(rèn)笛卡爾坐標(biāo)系(坐標(biāo)范圍-1.0~1.0)在屏幕上渲染幾何圖形
shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
提交著色器
triangleBatch.Draw();
在開始的設(shè)置openGL
窗口的時(shí)候烛芬,我們指定要一個(gè)雙緩沖區(qū)的渲染環(huán)境。這就意味著將在后臺(tái)緩沖區(qū)進(jìn)行渲染飒责,渲染結(jié)束后交換給前臺(tái)赘娄,這種方式可以防止觀察者看到可能伴隨著動(dòng)畫幀與動(dòng)畫幀之間的閃爍的渲染過(guò)程。以下代碼表示將在后臺(tái)緩沖區(qū)進(jìn)行渲染宏蛉,然后在結(jié)束時(shí)交換到前臺(tái)遣臼。
glutSwapBuffers();
e、窗口改變大小
新建窗口或者窗口大小改變時(shí)觸發(fā)拾并,用來(lái)設(shè)置新的寬度和高度揍堰。其中0,0
代表窗口中視口的左下角坐標(biāo)。
void ChangeSize(int w,int h)
{
glViewport(0,0, w, h);
}
2嗅义、繪制正方形
a屏歹、添加的屬性
blockSize
表示邊長(zhǎng)的一半(正方形頂點(diǎn)到Y(jié)的距離)
GLfloat blockSize = 0.1f;
正方形的4個(gè)點(diǎn)坐標(biāo)
GLfloat vVerts[] =
{
-blockSize,-blockSize,0.0f,
blockSize,-blockSize,0.0f,
blockSize,blockSize,0.0f,
-blockSize,blockSize,0.0f
};
b、修改用于渲染的數(shù)據(jù)
void SetupRC()
{
glClearColor(0.0f,0.0f,1.0f,1.0f);
shaderManager.InitializeStockShaders();
// 修改為GL_TRIANGLE_FAN 之碗,4個(gè)頂點(diǎn)
triangleBatch.Begin(GL_TRIANGLE_FAN,4);
triangleBatch.CopyVertexData3f(vVerts);
triangleBatch.End();
}
c蝙眶、修改程序入口函數(shù)
int main(int argc,char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow("Square");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
// 注冊(cè)特殊鍵位響應(yīng)函數(shù)
glutSpecialFunc(SpecialKeys);
GLenum err = glewInit();
if(GLEW_OK != err)
{
fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
return 0;
}
d、實(shí)現(xiàn)特殊鍵位響應(yīng)函數(shù)
設(shè)置移動(dòng)步?和相對(duì)移動(dòng)頂點(diǎn)D
void SpecialKeys(int key, int x, int y)
{
// 移動(dòng)步?
GLfloat stepSize = 0.025f;
// 相對(duì)移動(dòng)頂點(diǎn)D
GLfloat blockX = vVerts[0];
GLfloat blockY = vVerts[10];
printf("v[0] = %f\n",blockX);
printf("v[10] = %f\n",blockY);
......
}
根據(jù)鍵盤標(biāo)記判斷此時(shí)正方形移動(dòng)?向(上下左右)褪那,然后根據(jù)方向調(diào)整相對(duì)移動(dòng)坐標(biāo)(blockX/blockY
)值
if (key == GLUT_KEY_UP)// 上
{
blockY += stepSize;
}
if (key == GLUT_KEY_DOWN)// 下
{
blockY -= stepSize;
}
if (key == GLUT_KEY_LEFT)// 左
{
blockX -= stepSize;
}
if (key == GLUT_KEY_RIGHT)// 右
{
blockX += stepSize;
}
觸碰到邊界(4個(gè)邊界)的處理
if (blockX < -1.0f)
{
blockX = -1.0f;
}
// 當(dāng)正方形移動(dòng)到最右邊時(shí)
// 1.0 - blockSize * 2 = 總邊長(zhǎng) - 正方形的邊長(zhǎng) = 最左邊點(diǎn)的位置
if (blockX > (1.0 - blockSize * 2))
{
blockX = 1.0f - blockSize * 2;
}
// 當(dāng)正方形移動(dòng)到最下面時(shí)
// -1.0 - blockSize * 2 = Y(負(fù)軸邊界) - 正方形邊長(zhǎng) = 最下面點(diǎn)的位置
if (blockY < -1.0f + blockSize * 2 )
{
blockY = -1.0f + blockSize * 2;
}
// 當(dāng)正方形移動(dòng)到最上面時(shí)
if (blockY > 1.0f)
{
blockY = 1.0f;
}
printf("blockX = %f\n",blockX);
printf("blockY = %f\n",blockY);
根據(jù)相對(duì)頂點(diǎn)D計(jì)算出ABCD每個(gè)頂點(diǎn)坐標(biāo)值(只需要更新X幽纷、Y坐標(biāo),不需要更新Z坐標(biāo))
vVerts[0] = blockX;
vVerts[1] = blockY - blockSize*2;
printf("(%f,%f)\n",vVerts[0],vVerts[1]);
vVerts[3] = blockX + blockSize*2;
vVerts[4] = blockY - blockSize*2;
printf("(%f,%f)\n",vVerts[3],vVerts[4]);
vVerts[6] = blockX + blockSize*2;
vVerts[7] = blockY;
printf("(%f,%f)\n",vVerts[6],vVerts[7]);
vVerts[9] = blockX;
vVerts[10] = blockY;
printf("(%f,%f)\n",vVerts[9],vVerts[10]);
更新頂點(diǎn)坐標(biāo)數(shù)組
triangleBatch.CopyVertexData3f(vVerts);
手動(dòng)觸發(fā)重新渲染
glutPostRedisplay();
三博敬、OpenGL的圖元類型
1友浸、各種需要的類
變化管線使用矩陣堆棧
GLShaderManager shaderManager;// 存儲(chǔ)著色器管理工具類
GLMatrixStack modelViewMatrix;// 模型視圖矩陣
GLMatrixStack projectionMatrix;// 投影矩陣
GLFrame cameraFrame;// 設(shè)置觀察者視圖坐標(biāo)
GLFrame objectFrame;// 設(shè)置物體視圖坐標(biāo)
投影矩陣
// 設(shè)置圖元繪制時(shí)候的投影方式
GLFrustum viewFrustum;
容器類(7種不同的圖元對(duì)應(yīng)7種容器對(duì)象)
GLBatch pointBatch;// 點(diǎn)
GLBatch lineBatch;// 線段
GLBatch lineStripBatch;// 線帶
GLBatch lineLoopBatch;// 線環(huán)
GLBatch triangleBatch;// 三角形
GLBatch triangleStripBatch;// 六邊形
GLBatch triangleFanBatch;// 柱體
幾何變換的管道
GLGeometryTransform transformPipeline;// 變換管道。存儲(chǔ)模型視圖/投影矩陣
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };// 綠色
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };// 黑色
跟蹤效果步驟
int nStep = 0;
2偏窝、點(diǎn) GL_POINTS
a收恢、簡(jiǎn)介
每個(gè)特定的頂點(diǎn)在屏幕上都僅僅是一個(gè)單獨(dú)的點(diǎn)。我們可通過(guò)調(diào)用glPointSize
改變默認(rèn)點(diǎn)的大小囚枪。
void glPointSize(GLfloat size);
b派诬、使用窗口維度設(shè)置視口和投影矩陣
void ChangeSize(int w, int h)
{
.......
}
設(shè)置視口
glViewport(0, 0, w, h);
創(chuàng)建投影矩陣,并將它載入投影矩陣堆棧中
// viewFrustum是投影矩陣链沼,SetPerspective表示立體圖形使用透視投影
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
// 獲得投影矩陣加載到投影矩陣堆棧中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
調(diào)用頂部載入單元矩陣
// 模型視圖矩陣加載單元矩陣
modelViewMatrix.LoadIdentity();
c默赂、設(shè)置頂點(diǎn)數(shù)據(jù)
void SetupRC()
{
......
}
灰色的背景
glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
設(shè)置變換管線以使用兩個(gè)矩陣堆棧
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
cameraFrame.MoveForward(-15.0f);
定義一些點(diǎn),三角形形狀
GLfloat vCoast[9] =
{
3,3,0,
0,3,0,
3,0,0
};
用點(diǎn)的形式
pointBatch.Begin(GL_POINTS, 3);
pointBatch.CopyVertexData3f(vCoast);
pointBatch.End();
d括勺、渲染場(chǎng)景
void RenderScene()
{
.......
}
壓棧
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
矩陣乘以矩陣堆棧的頂部矩陣缆八,相乘的結(jié)果隨后簡(jiǎn)存儲(chǔ)在堆棧的頂部
modelViewMatrix.MultMatrix(mCamera);
只要使用 GetMatrix
函數(shù)就可以獲取矩陣堆棧頂部的值
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
矩陣乘以矩陣堆棧的頂部矩陣曲掰,相乘的結(jié)果隨后簡(jiǎn)存儲(chǔ)在堆棧的頂部
modelViewMatrix.MultMatrix(mObjectFrame);
- 參數(shù)1:平面著色器
-
參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè)
4 * 4
變換矩陣
transformPipeline.GetModelViewProjectionMatrix()
獲取的
GetMatrix
函數(shù)就可以獲得矩陣堆棧頂部的值 - 參數(shù)3:顏色值(黑色)
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
設(shè)置點(diǎn)的大小
switch(nStep)
{
case 0:
// 設(shè)置點(diǎn)的大小
glPointSize(4.0f);
// 進(jìn)行繪圖
pointBatch.Draw();
// 恢復(fù)點(diǎn)的大小
glPointSize(1.0f);
break;
}
還原到以前的模型視圖矩陣(單位矩陣)
modelViewMatrix.PopMatrix();
進(jìn)行緩沖區(qū)交換
glutSwapBuffers();
e、圍繞一個(gè)指定的X,Y,Z軸旋轉(zhuǎn)
m3dDegToRad
表示角度轉(zhuǎn)化為弧度奈辰。上下圍繞X軸旋轉(zhuǎn)栏妖,左右圍繞Y軸旋轉(zhuǎn)
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP) objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN) objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT) objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT) objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
// 重新渲染
glutPostRedisplay();
}
f、根據(jù)空格次數(shù)奖恰,切換不同的“窗口名稱”
void KeyPressFunc(unsigned char key, int x, int y)
{
if (key == 32)
{
nStep++;
if(nStep > 6) nStep = 0;
}
switch(nStep)
{
case 0:
glutSetWindowTitle("GL_POINTS");
break;
case 1:
glutSetWindowTitle("GL_LINES");
break;
case 2:
glutSetWindowTitle("GL_LINE_STRIP");
break;
case 3:
glutSetWindowTitle("GL_LINE_LOOP");
break;
case 4:
glutSetWindowTitle("GL_TRIANGLES");
break;
case 5:
glutSetWindowTitle("GL_TRIANGLE_STRIP");
break;
case 6:
glutSetWindowTitle("GL_TRIANGLE_FAN");
break;
}
glutPostRedisplay();
}
2吊趾、線 GL_LINES
a、簡(jiǎn)介
線段就是2個(gè)頂點(diǎn)之間繪制的瑟啃。
// 設(shè)置獨(dú)立線段寬度為1.5f
glLineWidth(1.5f);
b论泛、修改設(shè)置數(shù)據(jù)源
// 通過(guò)線段的形式
lineBatch.Begin(GL_LINES, 3);
lineBatch.CopyVertexData3f(vCoast);
lineBatch.End();
c、修改渲染場(chǎng)景
case 1:
// 設(shè)置線的寬度
glLineWidth(2.0f);
// 進(jìn)行繪圖
lineBatch.Draw();
// 恢復(fù)線的寬度
glLineWidth(1.0f);
break;
3蛹屿、條帶線 GL_LINE_STRIP
為了把圖形連接起來(lái)屁奏,每個(gè)連接的頂點(diǎn)會(huì)被選定2次。一次作為線段的終點(diǎn)错负、一次作為下一條線段的起點(diǎn)坟瓢。
a、修改設(shè)置數(shù)據(jù)源
lineStripBatch.Begin(GL_LINE_STRIP, 3);
lineStripBatch.CopyVertexData3f(vCoast);
lineStripBatch.End();
b犹撒、修改渲染場(chǎng)景
case 2:
glLineWidth(2.0f);
lineStripBatch.Draw();
glLineWidth(1.0f);
break;
4折联、循環(huán)線 GL_LINE_LOOP
在線帶的基礎(chǔ)上額外增加了一條連接著一批次中最后一個(gè)點(diǎn)和第一個(gè)點(diǎn)的線段。
a油航、修改設(shè)置數(shù)據(jù)源
lineLoopBatch.Begin(GL_LINE_LOOP, 3);
lineLoopBatch.CopyVertexData3f(vCoast);
lineLoopBatch.End();
b崭庸、修改渲染場(chǎng)景
case 3:
glLineWidth(2.0f);
lineLoopBatch.Draw();
glLineWidth(1.0f);
break;
5、獨(dú)立三角形 GL_TRIANGLES
a谊囚、修改設(shè)置數(shù)據(jù)源
每3個(gè)頂點(diǎn)定義一個(gè)新的三角形
GLfloat vPyramid[12][3] =
{
-2.0f, 0.0f, -2.0f,
2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, -2.0f,
2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, 2.0f,
0.0f, 4.0f, 0.0f,
-2.0f, 0.0f, 2.0f,
-2.0f, 0.0f, -2.0f,
0.0f, 4.0f, 0.0f
};
GL_TRIANGLES
通過(guò)三角形創(chuàng)建金字塔
triangleBatch.Begin(GL_TRIANGLES, 12);
triangleBatch.CopyVertexData3f(vPyramid);
triangleBatch.End();
b怕享、修改渲染場(chǎng)景
case 4:
DrawWireFramedBatch(&triangleBatch);
break;
畫綠色部分
void DrawWireFramedBatch(GLBatch* pBatch)
{
// 修改顏色為綠色
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
pBatch->Draw();
.......
}
c、畫黑色邊框
偏移深度镰踏。在同一位置要繪制填充和邊線函筋,會(huì)產(chǎn)生z沖突,所以要偏移
glPolygonOffset(-1.0f, -1.0f);
用于啟用各種功能
-
GL_POLYGON_OFFSET_LINE:根據(jù)函數(shù)
glPolygonOffset
的設(shè)置奠伪,啟用線的深度偏移 - GL_LINE_SMOOTH:執(zhí)行后跌帐,過(guò)濾線點(diǎn)的鋸齒
- GL_BLEND:啟用顏色混合,例如實(shí)現(xiàn)半透明效果
- GL_DEPTH_TEST:啟用深度測(cè)試绊率,根據(jù)坐標(biāo)的遠(yuǎn)近自動(dòng)隱藏被遮住的圖形
-
注意:
glEnable()
不能寫在glBegin()
和glEnd()
中間
glEnable(GL_POLYGON_OFFSET_LINE);
畫反鋸齒谨敛,讓黑邊好看些
glEnable(GL_LINE_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
通過(guò)調(diào)用glPolygonMode
將多邊形正面或者背面設(shè)為線框模式,實(shí)現(xiàn)線框渲染
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
設(shè)置線條寬度
glLineWidth(2.5f);
著色器
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
復(fù)原原本的設(shè)置滤否。通過(guò)調(diào)用glPolygonMode
將多邊形正面或者背面設(shè)為全部填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
6脸狸、三角形條帶 GL_TRIANGLE_STRIP
對(duì)于很多表面和形狀來(lái)說(shuō),我們可能需要繪制幾個(gè)相連的三角形。我們可以使用GL_TRIANGLE_STRIP圖元繪制一串相連的三角形炊甲。
a泥彤、修改設(shè)置數(shù)據(jù)源
設(shè)置頂點(diǎn)個(gè)數(shù)、半徑卿啡、原點(diǎn)
GLfloat vPoints[100][3];
// 頂點(diǎn)個(gè)數(shù)
int nVerts = 0;
// 半徑
GLfloat r = 3.0f;
// 原點(diǎn)的(x,y,z) = (0,0,0);
vPoints[nVerts][0] = 0.0f;
vPoints[nVerts][1] = 0.0f;
vPoints[nVerts][2] = 0.0f;
M3D_2PI
就是2Pi的意思吟吝,繪制成的圓形等分成6份。數(shù)組下標(biāo)自增(每自增1次就表示一個(gè)頂點(diǎn))
for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f)
{
// 數(shù)組下標(biāo)自增(每自增1次就表示一個(gè)頂點(diǎn))
nVerts++;
// x點(diǎn)坐標(biāo) = cos(angle) * 半徑
vPoints[nVerts][0] = float(cos(angle)) * r;
// y點(diǎn)坐標(biāo) = sin(angle) * 半徑
vPoints[nVerts][1] = float(sin(angle)) * r;
// z點(diǎn)的坐標(biāo)
vPoints[nVerts][2] = -0.5f;
}
b颈娜、修改渲染場(chǎng)景
case 5:
DrawWireFramedBatch(&triangleFanBatch);
break;
7剑逃、三角形扇面 GL_TRIANGLE_FAN
使用GL_TRIANGLE_FAN
創(chuàng)建一組圍繞一個(gè)中心點(diǎn)的相連三角形。
a官辽、修改設(shè)置數(shù)據(jù)源
頂點(diǎn)下標(biāo)和半徑
// 頂點(diǎn)下標(biāo)
int iCounter = 0;
// 半徑
GLfloat radius = 3.0f;
從0度~360度炕贵,以0.3弧度為步長(zhǎng)
for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
{
// 圓形的頂點(diǎn)的X,Y
GLfloat x = radius * sin(angle);
GLfloat y = radius * cos(angle);
......
}
繪制2個(gè)三角形(他們的x,y頂點(diǎn)一樣,只是z點(diǎn)不一樣)
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = x;
vPoints[iCounter][1] = y;
vPoints[iCounter][2] = 0.5;
iCounter++;
結(jié)束循環(huán)野崇,在循環(huán)位置生成2個(gè)三角形
vPoints[iCounter][0] = vPoints[0][0];
vPoints[iCounter][1] = vPoints[0][1];
vPoints[iCounter][2] = -0.5;
iCounter++;
vPoints[iCounter][0] = vPoints[1][0];
vPoints[iCounter][1] = vPoints[1][1];
vPoints[iCounter][2] = 0.5;
iCounter++;
GL_TRIANGLE_STRIP
共用一個(gè)條帶(strip
)上的頂點(diǎn)的一組三角形
triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
triangleStripBatch.CopyVertexData3f(vPoints);
triangleStripBatch.End();
b、修改渲染場(chǎng)景
case 6:
DrawWireFramedBatch(&triangleStripBatch);
break;
8亩钟、其他
a乓梨、GLBatch 批次容器
GLTools
庫(kù)中包含一個(gè)簡(jiǎn)單的容器類,叫做 GLBatch
清酥。這個(gè)類可以作為7種圖元的簡(jiǎn)單批次容器使用扶镀。而且它知道在使用GL_ShaderManager
支持的任意存儲(chǔ)著色器時(shí)如何對(duì)圖元進(jìn)行渲染。使用 GLBatch
類非常簡(jiǎn)單焰轻。首先對(duì)批次進(jìn)行初始化臭觉,告訴這個(gè)類它代表哪種圖元。
參數(shù)1:圖元
參數(shù)2:頂點(diǎn)數(shù)
參數(shù)3:一組或者兩組紋理坐標(biāo)(可選)
void GLBatch::Begain(GLeunm primitive,GLuint nVerts,GLuint nTexttureUnints = 0);
然后辱志,至少要復(fù)制一個(gè)由3分量(x, y, z)頂點(diǎn)組成的數(shù)組蝠筑。
void GLBatch::CopyVertexData3f(GLfloat *vVerts);
還可以選擇復(fù)制表面法線、顏色和紋理坐標(biāo)揩懒。
//復(fù)制表面法線
void GLBatch::CopyNormalDataf(GLfloat *vNorms);
//復(fù)制顏色
void GLBatch::CopyColorData4f(GLfloat *vColors);
//復(fù)制紋理坐標(biāo)
void GLBatch::CopyTexCoordData2f(GLFloat *vTextCoords,GLuint uiTextureLayer);
結(jié)束繪制
void GLBatch::End(void);
b什乙、矩陣堆棧GLMatrixStack
變化管線使用矩陣堆棧GLMatrixStack
。它的構(gòu)造函數(shù)允許指定堆棧的最大深度已球,默認(rèn)的堆棧深度為64臣镣。
GLMatrixStack(int iStackDepth = 64);
這個(gè)矩陣堆在初始化時(shí)已經(jīng)在堆棧中包含了單位矩陣。通過(guò)調(diào)用頂部載入單位矩陣智亮。
void GLMatrixStack::LoadIndentiy(void);
在堆棧頂部載入任何矩陣忆某。
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
四、使用存儲(chǔ)著色器
在OpenGL 核心框架中阔蛉,并沒(méi)提供任何內(nèi)建渲染管線弃舒,在提交一個(gè)幾何圖形進(jìn)行渲染之前,必須實(shí)現(xiàn)一個(gè)著色器馍忽。著色器由GLTools
的 C++ 類GLShaderManager
管理棒坏。他們能夠滿足進(jìn)行基本渲染的基本要求燕差,要求不高的程序員,這些存儲(chǔ)著色器已經(jīng)足以滿足他們的需求坝冕。但隨著時(shí)間和經(jīng)驗(yàn)的提升徒探,大部分開發(fā)者可能不滿足于此,會(huì)著手去寫著色器喂窟。
1测暗、著色器的使用
a、GLShaderManager 的初始化
GLShaderManager shaderManager;
shaderManager.InitializeStockShaders();
b磨澡、GLShaderManager 屬性
存儲(chǔ)著色器為每個(gè)變量都使用一致的內(nèi)部變量命名規(guī)則和相同的屬性槽(Attribute Slot
)碗啄。
標(biāo)識(shí)符 | 描述 |
---|---|
GLT_ATTRIBUTE_VERTEX | 3分量(x, y, z)頂點(diǎn)位置 |
GLT_ATTRIBUTE_COLOR | 4分量(r, g, b, a)顏色值 |
GLT_ATTRIBUTE_NORMAL | 3分量(x, y, z)表面法線 |
GLT_ATTRIBUTE_TEXTURE0 | 第一對(duì) 2 分量(s ,t)紋理坐標(biāo) |
GLT_ATTRIBUTE_TEXTURE1 | 第二對(duì) 2 分量(s ,t)紋理坐標(biāo) |
c、GLShanderManager 的 uniform 值
要對(duì)幾何圖形進(jìn)行渲染稳摄,我們需要為讀寫遞交屬性矩陣稚字,首先要綁定到我們想要使用的著色程序上,并提供程序的 Uniform
值厦酬。GLShanderManager
類可以為我們完成這項(xiàng)工作胆描。useStockShader
函數(shù)會(huì)選擇一個(gè)存儲(chǔ)著色器并提供這個(gè)著色器的 Uniform
值,這些工作通過(guò)一次函數(shù)調(diào)用就能完成:
GLShaderManager::UseStockShader(GLenum shader, ... ...);
在 C 語(yǔ)言(或 C++ 語(yǔ)言)中仗阅,......表示函數(shù)接受一個(gè)可變的參數(shù)數(shù)量昌讲。就這個(gè)函數(shù)本身而言,它根據(jù)我們選擇的著色器减噪,從堆棧中提取正確的參數(shù)短绸,這些參數(shù)就是特定著色器要求的 Uniform
值。
2筹裕、著色器種類
單位(Identity)著色器 GLT_SHADER_IDENTITY
單位(Identity
)著色器只是簡(jiǎn)單地使用默認(rèn)的笛卡爾坐標(biāo)系(坐標(biāo)范圍-1.0~1.0)醋闭。所有片段都應(yīng)用同一種顏色,結(jié)合圖形為實(shí)心和未渲染的饶碘。這種著色器只使用一個(gè)屬性 GLT_ATTRIBUTE_VERTEX
目尖。vColor
參數(shù)包含了要求的顏色。
參數(shù)1:?jiǎn)挝恢?參數(shù)2:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_IDENTITY, GLfoat vColor[4]);
平面(Flat)著色器 GLT_SHADER_FLAT
平面(Flat
)著色器將單位著色器進(jìn)行了擴(kuò)展扎运,允許為集合圖形變換指定一個(gè) 4 x 4 的變換矩陣瑟曲。經(jīng)常被稱作“模型師徒投影矩陣”。這種著色器只使用一個(gè)屬性 GLT_ATTRIBUTE_VERTEX
豪治。
參數(shù)1:平面著色器
參數(shù)2:允許變化的4*4矩陣
參數(shù)3:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_FLAT, GLfoat mvp[16], GLfloat vColor[4]);
上色(Shaded)著色器 GLT_SHADER_SHADED
上色(Shaded
)著色器:唯一的 Uniform
值就是在幾何圖形中應(yīng)用的變換矩陣洞拨。GLT_ATTRIBUTE_VERTEX
和 GLT_ATTRIBUTE_COLOR
在這種著色器中都會(huì)使用。顏色值將被平滑地插入頂點(diǎn)之間(稱為平滑著色)负拟。
GLShaderManager::UseStockShader(GLT_SHADER_SHADED, GLfoat mvp[16]);
默認(rèn)光源著色器 GLT_SHADER_DEFAULT_LIGHT
默認(rèn)光源著色器這種著色器使對(duì)象產(chǎn)生陰影和光照的效果烦衣。需要模型視圖矩陣、投影矩陣和作為基本色的顏色值等 Uniform
值。所需的屬性有 GLT_ATTRIBUTE_VERTEX
(頂點(diǎn)分量)和 GLT_ATTRIBUTE_NORMAL
(表面法線)花吟。
點(diǎn)光源著色器 GLT_SHADER_POINT_LIGHT_DIFF
點(diǎn)光源著色器和默認(rèn)光源著色器很相似秸歧,但光源位置可能是待定的。接受 4 個(gè) Uniform
值衅澈,即模型視圖矩陣键菱、投影矩陣、視點(diǎn)坐標(biāo)系中的光源位置和對(duì)象的基本漫反射顏色今布。同樣所需的屬性有 GLT_ATTRIBUTE_VERTEX
(頂點(diǎn)分量)和 GLT_ATTRIBUTE_NORMAL
(表面法線)经备。
參數(shù)1:點(diǎn)光源著色器
參數(shù)2:模型視圖矩陣
參數(shù)3:投影矩陣
參數(shù)4:視點(diǎn)坐標(biāo)系中的光源位置
參數(shù)5:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, GLfoat mvMatrix[16], GLfloat pMatrix[16], GLfloat vLightPos[3], GLfloat vColor[4]);
紋理替換矩陣 GLT_SHADER_TEXTURE_REPLACE
紋理替換矩陣著色器通過(guò)給定的模型視圖投影矩陣,使用綁定到 nTextureUnit
(紋理單元) 指定的紋理單元的紋理對(duì)幾何圖形進(jìn)行變換部默。片段顏色是從紋理樣本中直接獲取的侵蒙。所需的屬性有 GLT_ATTRIBUTE_VERTEX
(頂點(diǎn)分量)和 GLT_ATTRIBUTE_NORMAL
(表面法線)。
GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_MODULATE, GLfoat mvMatrix[16], GLfloat vColor, GLint nTextureUnit);
紋理調(diào)整著色器 GLT_SHADER_TEXTURE_MODULATE
紋理調(diào)整著色器這種著色器將一個(gè)基本色乘以一個(gè)取自紋理單元 nTextureUnit
的紋理傅蹂。所需的屬性有 GLT_ATTRIBUTE_VERTEX
(頂點(diǎn)分量)和 GLT_ATTRIBUTE_TEXTURE0
(紋理坐標(biāo))纷闺。
GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_MODULATE, GLfoat mvMatrix[16], GLfloat vColor, GLint nTextureUnit);
紋理光源著色器 GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF
紋理光源著色器著色器將一個(gè)紋理通過(guò)漫反射照明計(jì)算進(jìn)行調(diào)整(相乘),光線在視覺(jué)空間中的位置是給定的份蝴。這種著色器接受 5 個(gè) Uniform
值急但,即模型視圖矩陣、投影矩陣搞乏、視覺(jué)空間中的光源位置、幾何圖形的基本色和將要使用的紋理單元戒努。所需的屬性有 GLT_ATTRIBUTE_VERTEX
(頂點(diǎn)分量)请敦、GLT_ATTRIBUTE_NORMAL
(表面法線)和 GLT_ATTRIBUTE_TEXTURE0
(紋理坐標(biāo))。
參數(shù)1:紋理光源著色器
參數(shù)2:模型視圖矩陣
參數(shù)3:投影矩陣
參數(shù)4:視點(diǎn)坐標(biāo)系中的光源位置
參數(shù)5:幾何圖形的基本色
參數(shù)6:將要使用的紋理單元
GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, GLfloat mvMatrix, GLfoat mvMatrix[16], GLfloat vLightPos[3], GLfloat vBaseColor[4], GLint nTextureUnit);
五储玫、OpenGL 渲染技巧
用到的屬性
設(shè)置角色幀侍筛,作為相機(jī)
GLFrame viewFrame;
標(biāo)記:背面剔除、深度測(cè)試
int iCull = 0;
int iDepth = 0;
1撒穷、繪制甜甜圈
a匣椰、改變尺寸
void ChangeSize(int w, int h)
{
}
防止h變?yōu)?
if(h == 0) h = 1;
設(shè)置視口窗口尺寸
glViewport(0, 0, w, h);
setPerspective
函數(shù)的參數(shù)是一個(gè)從頂點(diǎn)方向看去的視場(chǎng)角度(用角度值表示)。設(shè)置透視模式端礼,初始化其透視矩陣禽笑。
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
把透視矩陣加載到透視矩陣對(duì)陣中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
初始化渲染管線
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
b、設(shè)置數(shù)據(jù)源
void SetupRC()
{
}
設(shè)置背景顏色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
初始化著色器管理器
shaderManager.InitializeStockShaders();
將相機(jī)向后移動(dòng)7個(gè)單元:肉眼到物體之間的距離
viewFrame.MoveForward(7.0);
創(chuàng)建一個(gè)甜甜圈
-
參數(shù)1:
GLTriangleBatch
容器幫助類 - 參數(shù)2:外邊緣半徑
- 參數(shù)3:內(nèi)邊緣半徑
- 參數(shù)4蛤奥、5:主半徑和從半徑的細(xì)分單元數(shù)量
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
點(diǎn)的大小(方便點(diǎn)填充時(shí)佳镜,肉眼觀察)
glPointSize(4.0f);
c、渲染場(chǎng)景
void RenderScene()
{
}
清除窗口和深度緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
把攝像機(jī)矩陣壓入模型矩陣中
modelViewMatix.PushMatrix(viewFrame);
設(shè)置繪圖顏色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
使用默認(rèn)光源著色器凡桥,通過(guò)光源蟀伸、陰影效果體現(xiàn)立體效果
-
參數(shù)1:
GLT_SHADER_DEFAULT_LIGHT
默認(rèn)光源著色器 - 參數(shù)2:模型視圖矩陣
- 參數(shù)3:投影矩陣
- 參數(shù)4:基本顏色值
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
繪制
torusBatch.Draw();
出棧。繪制完成恢復(fù)
modelViewMatix.PopMatrix();
交換緩存區(qū)
glutSwapBuffers();
d、鍵位設(shè)置
鍵位設(shè)置啊掏,通過(guò)不同的鍵位對(duì)其進(jìn)行設(shè)置蠢络。控制Camera
的移動(dòng)迟蜜,從而改變視口刹孔。
void SpecialKeys(int key, int x, int y)
{
......
}
根據(jù)方向調(diào)整觀察者位置
if(key == GLUT_KEY_UP) viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN) viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT) viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT) viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
重新刷新
glutPostRedisplay();
2、正面&背面剔除
a小泉、存在的問(wèn)題
如果我們繪制一個(gè)由很多個(gè)三角形組成的實(shí)體對(duì)象芦疏,那么第一個(gè)繪制的三角形可能會(huì)被后面繪制的三角形覆蓋。如下圖這個(gè)像游泳圈似的模型微姊,其中一些三角形在游泳圈的背面酸茴,另一些在正面,正常我們應(yīng)該是看不到背面的(不考慮透明幾何體的特殊情況)兢交。這樣的話薪捍,三角形繪制的順序可能會(huì)一團(tuán)糟,就變成了下圖的樣子配喳。
對(duì)正面和背面三角形進(jìn)行區(qū)分的原因之一就是為了進(jìn)行剔除酪穿。背面剔除能極大提高性能,避免上圖出現(xiàn)的問(wèn)題晴裹。它很高效被济,在渲染的圖元裝配階段就整體拋棄了一些三角形。
開啟背面剔除
glEnable(GL_CULL_FACE);
關(guān)閉背面剔除
glDisable(GL_CULL_FACE);
我們并沒(méi)有指明剔除的是正面還是背面涧团,這是由另外一個(gè)函數(shù) glCullFace
控制的
void glCullFace(GLenum mode);
mode
參數(shù)的可用值為 GL_FRONT
只磷、GL_BACK
或 GL_FRONT_AND_BACK
。這樣要消除不透明物體的內(nèi)部幾何圖形就需要兩行代碼:
void glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
在某些情況下泌绣,剔除實(shí)體幾何體的正面也很有必要钮追,比如要顯示圖形內(nèi)部渲染的時(shí)候。在渲染透明對(duì)象時(shí)阿迈,我們經(jīng)常會(huì)對(duì)一個(gè)對(duì)象進(jìn)行兩次渲染元媚,第一次會(huì)開啟透明并剔除正面,第二次則消除背面苗沧。這樣就在渲染正面之前渲染了背面刊棕,這也是渲染透明物體的需要。
但是在開啟背面剔除后待逞,會(huì)發(fā)現(xiàn)上面的游泳圈模型還是顯示的有問(wèn)題鞠绰,如圖:
這是因?yàn)闆](méi)有開啟深度測(cè)試。
b飒焦、在入口函數(shù)中添加右擊菜單欄
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("深度測(cè)試",1);
glutAddMenuEntry("正背面剔除",2);
glutAddMenuEntry("顏色填充", 3);
glutAddMenuEntry("線段填充", 4);
glutAddMenuEntry("點(diǎn)填充", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
菜單欄函數(shù)的實(shí)現(xiàn)方式如下
void ProcessMenu(int value)
{
switch(value)
{
case 1:
iDepth = !iDepth;
break;
case 2:
iCull = !iCull;
break;
case 3:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 4:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 5:
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
break;
}
// 重新渲染
glutPostRedisplay();
}
c蜈膨、在渲染場(chǎng)景中開啟/關(guān)閉正背面剔除功能
if (iCull)
{
glEnable(GL_CULL_FACE);
// glFrontFace(GL_CCW);
glCullFace(GL_BACK);
}
else
{
glDisable(GL_CULL_FACE);
}
3屿笼、深度測(cè)試
a、介紹深度測(cè)試
深度就是在openGL坐標(biāo)系中翁巍,像素點(diǎn)的 Z 坐標(biāo)距離觀察者的距離驴一。觀察者可能放在坐標(biāo)系的任何位置,那么灶壶,就不能簡(jiǎn)單的說(shuō) Z 數(shù)值越大或越小肝断,就是越靠近觀察者。如果觀察者在Z軸的正方向驰凛,Z 值大的靠近觀察者胸懈,如果是在Z軸的反方向,則 Z 值小的更靠近觀察者恰响。
深度緩沖區(qū)原理就是把一個(gè)距離觀察平面(近裁剪面)的深度值(或距離)與窗口中的每個(gè)像素相關(guān)聯(lián)趣钱。首先,使用glClear(GL_DEPTH_BUFFER_BIT)
把所有像素的深度值設(shè)置為最大值胚宦。如果啟用了深度緩沖區(qū)首有,在繪制每個(gè)像素之前,OpenGL會(huì)把它的深度值和已經(jīng)存儲(chǔ)在這個(gè)像素的深度值進(jìn)行比較枢劝。如果井联,新像素深度值 < 原先像素深度值,則新像素值會(huì)取代原先的您旁;反之烙常,新像素值被遮擋,它的顏色值和深度將被丟棄鹤盒。這個(gè)比較军掂、丟棄的過(guò)程就叫做 深度測(cè)試,深度測(cè)試是另一種高效消除隱藏面的技術(shù)昨悼。如果沒(méi)有深度緩沖區(qū),那么啟動(dòng)深度測(cè)試的命令將被忽略跃洛。
申請(qǐng)一個(gè)顏色緩沖區(qū)和一個(gè)深度緩沖區(qū):
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
要啟用深度測(cè)試率触,只需調(diào)用:
glEnable(GL_DEPTH_TEST);
關(guān)閉深度測(cè)試:
glDisable(GL_DEPTH_TEST);
在繪制場(chǎng)景前,清除顏色緩沖區(qū)和深度緩沖區(qū)汇竭。清除深度緩沖區(qū)的默認(rèn)值是1.0葱蝗,表示最大的深度值,深度值的范圍在[0,1]之間细燎。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
開啟了深度測(cè)試后两曼,我們終于得到了一個(gè)我們想要的游泳圈模型。
b玻驻、使用深度測(cè)試
根據(jù)設(shè)置iDepth
標(biāo)記來(lái)判斷是否開啟深度測(cè)試
if(iDepth)
{
glEnable(GL_DEPTH_TEST);
}
else
{
glDisable(GL_DEPTH_TEST);
}
4悼凑、混合
a偿枕、介紹混合
通常 OpenGL 渲染時(shí)會(huì)把顏色值放在顏色緩沖區(qū),每個(gè)像素的深度值放在深度緩沖區(qū)户辫。當(dāng)深度測(cè)試被關(guān)閉渐夸,新的顏色值會(huì)簡(jiǎn)單地覆蓋顏色緩沖區(qū)中已經(jīng)存在的其他值。當(dāng)深度測(cè)試被打開渔欢,則會(huì)保留深度值Z更小的墓塌。但如果打開了 混合功能,那么下層的顏色值就不會(huì)被清除了奥额。在打開混合功能的情況下苫幢,新的顏色會(huì)與已經(jīng)存在的顏色值在顏色緩沖區(qū)中進(jìn)行組合倔丈。
glEnable(GL_BLEND);// 開啟混合功能
- 目標(biāo)顏色:已經(jīng)存儲(chǔ)在顏色緩沖區(qū)中的顏色值迹蛤。
- 源顏色:作為當(dāng)前渲染命令的結(jié)果進(jìn)入顏色緩沖區(qū)的顏色值酱酬,它可能與目標(biāo)顏色進(jìn)行交互靡挥,也可能不交互械筛。
目標(biāo)顏色和源顏色都包含了單獨(dú)的紅滨达、綠蛛蒙、藍(lán)成分和一個(gè)可選的 alpha
值倔撞。如果我們忽略 alpha
值帚屉,OpenGL 會(huì)將它設(shè)為1.0谜诫。當(dāng)混合功能被啟用時(shí),源顏色和目標(biāo)顏色的組合方式是由混合方程式控制的攻旦。默認(rèn)情況下方程式如下:
// Cf:最終計(jì)算產(chǎn)生的顏色
// Cs:源顏色
// Cd:是目標(biāo)顏色
// S:源混合因子
// D:目標(biāo)混合因子
Cf = (CS * S) + (Cd * D)
這些混合因子是用glBlendFunc
函數(shù)進(jìn)行設(shè)置的喻旷。
// S和D都是枚舉值,不是可直接指定的實(shí)際值
glBlendFunc(GLenum S, GLenum D);
// 常見(jiàn)的混合函數(shù)組合
glBlendFun(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
例如牢屋,如果顏色緩沖區(qū)已經(jīng)有一種藍(lán)色(0.0f, 0.0f, 1.0f, 0.0f)
且预,這是目標(biāo)顏色 (Cd)
。如果在這上面用一種 alpha
值為 0.6 的 紅色(1.0f, 0.0f, 0.0f, 0.6f)
畫了一些什么東西烙无,就可以像下面這樣計(jì)算最終顏色锋谐。
Cd = 目標(biāo)顏色 = (0.0f, 0.0f, 1.0f, 0.0f)
Cs = 源顏色 = (1.0f, 0.0f, 0.0f, 0.6f)
S = 源 alpha 值 = 0.6
D = 1 減去 alpha 值 = 1.0 - 0.6 = 0.4
Cf = (Red * S)+ (Blue * 0.4)
最終的顏色是原先的藍(lán)色(目標(biāo)顏色)與后來(lái)的紅色(源顏色)進(jìn)行縮放后的組合。源顏色的 alpha 值越高截酷,添加的源顏色成分就越多涮拗,目標(biāo)顏色所保留的成分就越少。
未開啟混合時(shí)候的樣子
b迂苛、使用到的屬性
GLBatch squareBatch;// 正方形
GLBatch greenBatch;// 綠
GLBatch redBatch;// 紅
GLBatch blueBatch;// 藍(lán)
GLBatch blackBatch;// 黑
GLShaderManager shaderManager;
// 四個(gè)圖形的大小
GLfloat blockSize = 0.2f;
// 四個(gè)圖形的頂點(diǎn)
GLfloat vVerts[] = { -blockSize, -blockSize, 0.0f,
blockSize, -blockSize, 0.0f,
blockSize, blockSize, 0.0f,
-blockSize, blockSize, 0.0f};
c三热、設(shè)置數(shù)據(jù)源
void SetupRC()
{
}
繪制1個(gè)移動(dòng)矩形
squareBatch.Begin(GL_TRIANGLE_FAN, 4);
squareBatch.CopyVertexData3f(vVerts);
squareBatch.End();
繪制4個(gè)固定矩形
GLfloat vBlock[] = { 0.25f, 0.25f, 0.0f,
0.75f, 0.25f, 0.0f,
0.75f, 0.75f, 0.0f,
0.25f, 0.75f, 0.0f};
greenBatch.Begin(GL_TRIANGLE_FAN, 4);
greenBatch.CopyVertexData3f(vBlock);
greenBatch.End();
......
d、繪制場(chǎng)景
void RenderScene(void)
{
}
定義4種顏色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 0.5f };
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlue[] = { 0.0f, 0.0f, 1.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
繪制場(chǎng)景的時(shí)候三幻,使用單位著色器將4個(gè)固定矩形繪制好就漾。
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
greenBatch.Draw();
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
redBatch.Draw();
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
blueBatch.Draw();
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
blackBatch.Draw();
開啟混合
glEnable(GL_BLEND);
開啟組合函數(shù),計(jì)算混合顏色因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
使用著色器管理器
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
容器類開始繪制
squareBatch.Draw();
關(guān)閉混合功能
glDisable(GL_BLEND);
續(xù)文見(jiàn)下篇:iOS多媒體:OpenGL(下)
Demo
Demo在我的Github上念搬,歡迎下載抑堡。
Multi-MediaDemo