第一篇筆記 iOS OpenGL ES菜鳥(niǎo)學(xué)習(xí)筆記(1)——畫(huà)一個(gè)簡(jiǎn)單的三角形 其實(shí)已經(jīng)寫(xiě)了很久了,其實(shí)研究OpenGL ES是為了在iOS原生App中使用AR高蜂。最近一直比較忙猪勇,也沒(méi)有那么時(shí)間去鼓搗這個(gè)筆記踱承,終于研究得差不多了排作,也就回來(lái)這里繼續(xù)行騙了乡革。(ps:菜鳥(niǎo)學(xué)習(xí)筆記寇僧,如有不對(duì),大神請(qǐng)指教)
一沸版、準(zhǔn)備
從我們第一篇筆記上進(jìn)行更改
準(zhǔn)備一張圖嘁傀,什么圖都可以(不要是白色,白色的東西在白色的背景下還能看見(jiàn)嗎)
我這里用的一個(gè)郁金香的圖片
什么是紋理這個(gè)我在文章末尾稍微介紹一下视粮,有興趣的可以看看细办。
二、修改vertices
因?yàn)橐L制紋理上去,所以我們必須在vertices上加上紋理坐標(biāo)
// 這個(gè)數(shù)據(jù)類(lèi)型用于存儲(chǔ)每一個(gè)頂點(diǎn)數(shù)據(jù)
typedef struct {
GLKVector3 positionCoords; // 頂點(diǎn)數(shù)據(jù)
GLKVector2 textureCoords; // 紋理坐標(biāo)
} SceneVertex;
// 創(chuàng)建本例中要用到的三角形頂點(diǎn)數(shù)據(jù)
// 這里的數(shù)據(jù)比上一個(gè)例子新增了紋理數(shù)據(jù)
static const SceneVertex vertices[] =
{
{{-0.5, -0.5, 0.0}, {0.0, 0.0}}, // 左下
{{ 0.5, -0.5, 0.0}, {1.0, 0.0}}, // 右下
{{-0.5, 0.5, 0.0}, {0.0, 1.0}} // 左上
};
三、- (void)viewDidLoad()
在這里我們要將圖片實(shí)現(xiàn)讀入到緩存中(如果在- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect中讀入舱痘,那么將會(huì)浪費(fèi)很多資源,畢竟繪制一次讀入一次)
在第一篇文章的- (void)viewDidLoad()的末尾追加
// 讀入需要繪制的圖片的CGImageRef內(nèi)容
// 畢竟OpenGL是基于C++的嘛茴肥,你直接給一個(gè)UIImage人怎么識(shí)別
CGImageRef imageRefTulip = [UIImage imageNamed:@"tulip"].CGImage;
// 使用GLKTextureLoader(紋理讀取)從上邊得到的imageRefTulip讀取紋理信息
GLKTextureInfo *textureInfoTulip = [GLKTextureLoader textureWithCGImage:imageRefTulip
options:nil
error:nil];
// 將讀取到的紋理信息緩存到baseEffec的texture2d0中
self.baseEffect.texture2d0.name = textureInfoTulip.name;
self.baseEffect.texture2d0.target = textureInfoTulip.target;
從代碼備注中直接可以看書(shū)每句代碼干的事情荡灾。
GLKTextureInfo封裝了剛創(chuàng)建的紋理緩存相關(guān)的信息瓤狐,包括他的尺寸以及是否包含MIP貼圖。我們這里只需要OpenGL ES標(biāo)識(shí)符批幌、名字和用于紋理的OpenGL ES目標(biāo)础锐。
GLKTextureLoader會(huì)自動(dòng)調(diào)用glTexParameteri()方法來(lái)為創(chuàng)建的紋理緩存設(shè)置OpenGL ES取樣模式和循環(huán)模式。其實(shí)就是告訴OpenGL ES怎么處理可用紋素的數(shù)量與需要被著色的片源的數(shù)量不匹配的情況(基本不存在兩者相同的情況)逼裆。我們將在本文最后對(duì)此稍微介紹一下郁稍。
四、- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
這里做的事情跟第一篇筆記其實(shí)沒(méi)什么差別胜宇,只是多了一個(gè)紋理的繪制耀怜。
在執(zhí)行繪制操作之前加入如下代碼
// 繪制紋理數(shù)據(jù)準(zhǔn)備
glEnableVertexAttribArray(GLKVertexAttribTexCoord0); // 啟用紋理
glVertexAttribPointer(GLKVertexAttribTexCoord0,
2,
GL_FLOAT,
GL_FALSE,
sizeof(SceneVertex),
NULL + offsetof(SceneVertex, textureCoords));
其實(shí)到這里代碼就沒(méi)了恢着,是不是很少。
五财破、運(yùn)行
沒(méi)有源碼的博客不是好的示例
https://github.com/JunesYin/LyOpenGLES/tree/master/LyOpenGLES_02
六掰派、一點(diǎn)紋理知識(shí)
說(shuō)了要介紹就要介紹,一個(gè)字都不能少左痢。
1靡羡、什么是紋理
紋理是一個(gè)用來(lái)保存圖像的顏色元素值的OpenGL ES緩存。簡(jiǎn)單來(lái)說(shuō)俊性,渲染場(chǎng)景其實(shí)渲染很多個(gè)像素點(diǎn)略步,這些像素點(diǎn)都有各自的顏色。如果我們?cè)阡秩緯r(shí)針對(duì)每一個(gè)像素點(diǎn)指定顏色定页,這將會(huì)是一個(gè)非常龐大的工程量趟薄,試想一個(gè)像素點(diǎn)有多大,而iPhone的屏幕又有多少像素點(diǎn)典徊,就算我們渲染10x10的一個(gè)正方形圖像杭煎,這都需要指定100個(gè)像素點(diǎn)了。所以需要紋理貼圖卒落,使用紋理貼圖使我們只需要關(guān)注渲染多大的場(chǎng)景羡铲,然后將需要呈現(xiàn)的圖像繪制進(jìn)這個(gè)場(chǎng)景就可以了(以上觀點(diǎn)僅個(gè)人理解,如有錯(cuò)誤儡毕,歡迎評(píng)論指正)也切。上邊的例子就是將郁金香作為紋理貼圖貼在我們繪制的三角形中。
2妥曲、紋理坐標(biāo)系
紋理坐標(biāo)系有一個(gè)命名為S和T的2D軸贾费。如下圖,一個(gè)紋理中無(wú)論有多少個(gè)紋素檐盟,紋理的尺寸永遠(yuǎn)在S軸上從0.0到1.0,T軸也是從0.0到1.0押桃。從一個(gè)寬為2像素寬為16像素的圖像初始化來(lái)的紋理葵萎,整個(gè)S軸有2像素,T軸有16像素唱凯。
3羡忘、如何對(duì)齊紋理
雖然我們給OpenGL ES提供了一個(gè)紋理貼圖,但是它還是不知道要怎么利用我們給定的紋理對(duì)幾何圖形進(jìn)行著色磕昼。咱們先來(lái)看幾個(gè)名詞:
- 視口(viewport):幀緩存中的像素位置叫做視口卷雕。渲染時(shí),GPU會(huì)轉(zhuǎn)換純數(shù)學(xué)的OpenGL ES坐標(biāo)系中的X票从、Y漫雕、Z坐標(biāo)為幀緩存中所對(duì)應(yīng)真實(shí)的像素位置滨嘱。
- 點(diǎn)陣化(rasterizing):轉(zhuǎn)換幾何形狀數(shù)據(jù)為幀緩存中的顏色像素的渲染步驟叫做點(diǎn)陣化。
- 片元(fragment):每個(gè)顏色像素叫做片元浸间。
映射(mapping):指定怎么對(duì)其紋理和頂點(diǎn)太雨,以便讓GPU知道每個(gè)片元的顏色由哪些紋素決定。這通過(guò)擴(kuò)展為每個(gè)頂點(diǎn)保存的數(shù)據(jù)來(lái)實(shí)現(xiàn):除了X魁蒜、Y囊扳、Z坐標(biāo),每個(gè)頂點(diǎn)還給出了U和V坐標(biāo)值兜看。U坐標(biāo)映射到S軸的位置锥咸,V坐標(biāo)映射到T軸。不同的是{U细移,V}坐標(biāo)可能會(huì)超出0.0到1.0的這個(gè)范圍她君。
在每個(gè)頂點(diǎn)的X、Y葫哗、Z坐標(biāo)被轉(zhuǎn)換為視口坐標(biāo)后缔刹,GPU會(huì)設(shè)置轉(zhuǎn)換生成的三角形內(nèi)的每個(gè)像素的顏色。當(dāng)OpenGL ES沒(méi)有使用紋理時(shí)劣针,GPU會(huì)根據(jù)包含該片元的對(duì)象的頂點(diǎn)的顏色來(lái)計(jì)算每個(gè)片元的顏色校镐。使用紋理時(shí),GPU會(huì)根據(jù)當(dāng)前綁定的紋理緩存中的紋素來(lái)計(jì)算每個(gè)片元的顏色捺典。
Note:我們?cè)谏鲜隼又惺褂脅-0.5, -0.5, 0.0}鸟廓、{ 0.5, -0.5, 0.0}、{-0.5, 0.5, 0.0}這三個(gè)坐標(biāo)點(diǎn)指定了一個(gè)三角形的三個(gè)頂點(diǎn)襟己,這個(gè)三角形的寬度和高度在純數(shù)學(xué)的OpenGL ES坐標(biāo)系中是相等的引谜。但是我們?cè)诶L制三角形時(shí),幀緩存是按照像素來(lái)匹配屏幕坐標(biāo)的擎浴。將坐標(biāo)點(diǎn)轉(zhuǎn)換為視口坐標(biāo)员咽,這使得所有繪制的幾何圖形都被拉伸以適合屏幕大小,iPhone屏幕的比例均為高大于寬贮预,所以我們的例子中看起來(lái)高都大于寬贝室。如果要控制這個(gè)轉(zhuǎn)換,需要做一點(diǎn)操作仿吞,這個(gè)我們?cè)诤罄m(xù)文章會(huì)慢慢了解的滑频。
4、取樣模式
每個(gè)頂點(diǎn)的U和V坐標(biāo)會(huì)附加到每個(gè)頂點(diǎn)在視口坐標(biāo)中的最終位置唤冈。GPU會(huì)根據(jù)計(jì)算出來(lái)的每個(gè)片元的U峡迷、V位置從綁定的紋理中選擇紋素,這個(gè)過(guò)程叫做取樣
你虹。取樣會(huì)把紋理的S和T坐標(biāo)系與每個(gè)渲染的三角形的頂點(diǎn)的U绘搞、V坐標(biāo)匹配起來(lái)彤避。
如上圖所示,在S和T坐標(biāo)系中與{0.0看杭,0.0}位置最近的紋素會(huì)被映射到擁有{0.0忠藤, 0.0}的頂點(diǎn)U、V坐標(biāo)的頂點(diǎn)所對(duì)應(yīng)的片元上楼雹。每個(gè)隨后的片元位置對(duì)應(yīng)于一個(gè)沿著S和T軸的與該片元在U模孩、V坐標(biāo)中的位置等比例的位置。例如贮缅,一個(gè)U榨咐、V坐標(biāo)為{0.5, 0.5}的片元會(huì)被當(dāng)前綁定的紋理中最接近中間位置的紋素所著色谴供。
OpenGL ES支持多個(gè)不同的取樣模式块茁。當(dāng)一個(gè)三角形生成的片元少于綁定的紋理的可用紋素時(shí)會(huì)發(fā)生什么。一個(gè)含有大量紋素的紋理被映射到幀緩存內(nèi)一個(gè)只覆蓋幾個(gè)像素的三角形中桂肌,這種情況是很常見(jiàn)的数焊,相反的情況也很常見(jiàn)。到底是怎么才能比較好的將片元與紋素對(duì)應(yīng)起來(lái)呢崎场?
先看看如下幾個(gè)函數(shù)調(diào)用
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
4.1 GL_TEXTURE_MIN_FILTER
GL_TEXTURE_MIN_FILTER
參數(shù)適用于紋素過(guò)多的情況
- 使用
GL_LINEAR
指定這種情況時(shí)佩耳,出現(xiàn)多個(gè)紋素對(duì)應(yīng)一個(gè)片元時(shí),從相配的多個(gè)紋素中取樣顏色谭跨,使用線(xiàn)性?xún)?nèi)插法來(lái)混著這些顏色從而得到這個(gè)片元的顏色干厚,簡(jiǎn)
單來(lái)說(shuō)就是混合與當(dāng)前片元相配的所有紋素的顏色。這很可能出現(xiàn)一個(gè)紋理中不存在的顏色螃宙。 - 使用
GL_NEAREST
指定時(shí)蛮瞄,與該片元的U、V坐標(biāo)最接近的紋素的顏色將被作為片元的顏色谆扎。例如挂捅,一個(gè)紋理是由交替的黑色紋素和白色紋素組成,GL_NEAREST會(huì)拾取與相近的一個(gè)紋素或另一個(gè)紋素燕酷,此時(shí)最終片元的顏色要么是黑色籍凝,要么是白色。
4.2 GL_TEXTURE_MAG_FILTER
GL_TEXTURE_MAG_FILTER
參數(shù)適用于紋素不足的情況苗缩,此時(shí)將唯一地映射一個(gè)或多個(gè)紋素到每個(gè)片元上時(shí)配置取樣。
- 使用
GL_LINEAR
指定這種情況時(shí)声诸,OpenGL ES將混合附近紋素的顏色來(lái)計(jì)算片元的顏色酱讶。GL_LINEAR有一個(gè)放大紋理的效果并將其模糊地出現(xiàn)在渲染的三角形片元上。 - 使用
GL_NEAREST
指定時(shí)彼乌,OpenGL ES僅僅拾取與片元的U泻肯、V位置接近的紋素的顏色渊迁,并放大紋理,這讓紋素像素化地出現(xiàn)在渲染的三角形片元上灶挟。
Note:GLKTextureLoader會(huì)自動(dòng)設(shè)置GL_TEXTURE_MIN_FILTER為GL_LINEAR琉朽。
5、循環(huán)模式
除了減小和放大過(guò)濾選項(xiàng)稚铣,當(dāng)U箱叁、V坐標(biāo)的值小于0或者大于1時(shí),程序會(huì)指定發(fā)生什么惕医。這里有兩個(gè)選擇:
· 盡可能多地重復(fù)紋理以填滿(mǎn)映射到整個(gè)結(jié)合圖形的U耕漱、V坐標(biāo)區(qū)域;
· 每當(dāng)片元的U抬伺、V坐標(biāo)值超出紋理的S螟够、T坐標(biāo)系范圍時(shí),從紋理邊緣取樣紋素峡钓。
紋理的循環(huán)模式需要為S軸與T軸分開(kāi)設(shè)置妓笙,來(lái)看看這幾句代碼
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
第一句代碼:S軸,若片元的U坐標(biāo)超出紋理的S坐標(biāo)系范圍能岩,取樣紋理邊緣的紋素寞宫;
第二句代碼:S軸,若片元的U坐標(biāo)超出紋理的S坐標(biāo)系范圍捧灰,重復(fù)紋理淆九;
第三局代碼:T軸,若片元的V坐標(biāo)超出紋理的T坐標(biāo)系范圍毛俏,取樣紋理邊緣的紋素炭庙;
第四局代碼:T軸,若片元的V坐標(biāo)超出紋理的T坐標(biāo)系范圍煌寇,重復(fù)紋理焕蹄。
Note:GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T都會(huì)自動(dòng)被設(shè)置為GL_CLAMP_TO_EDGE。
系列源碼傳送門(mén):https://github.com/JunesYin/LyOpenGLES