OpenGL入門(九)-- OpenGL 紋理簡單介紹

今天旱捧,我們來了解了解紋理的知識,該篇文章轉(zhuǎn)載自這里踩麦。

紋理

  • 我們已經(jīng)了解到枚赡,我們可以為每個(gè)頂點(diǎn)添加顏色來增加圖形的細(xì)節(jié),從而創(chuàng)建出有趣的圖像谓谦。但是贫橙,如果想讓圖形看起來更真實(shí),我們就必須有足夠多的頂點(diǎn)反粥,從而指定足夠多的顏色卢肃。這將會(huì)產(chǎn)生很多額外開銷疲迂,因?yàn)槊總€(gè)模型都會(huì)需求更多的頂點(diǎn),每個(gè)頂點(diǎn)又需求一個(gè)顏色屬性莫湘。
  • 藝術(shù)家和程序員更喜歡使用紋理(Texture)尤蒿。紋理是一個(gè)2D圖片(甚至也有1D和3D的紋理),它可以用來添加物體的細(xì)節(jié)逊脯;你可以想象紋理是一張繪有磚塊的紙优质,無縫折疊貼合到你的3D的房子上,這樣你的房子看起來就像有磚墻外表了军洼。因?yàn)槲覀兛梢栽谝粡垐D片上插入非常多的細(xì)節(jié)巩螃,這樣就可以讓物體非常精細(xì)而不用指定額外的頂點(diǎn)。

除了圖像以外匕争,紋理也可以被用來儲存大量的數(shù)據(jù)避乏,這些數(shù)據(jù)可以發(fā)送到著色器上,但是這不是我們現(xiàn)在的主題甘桑。

下面你會(huì)看到之前教程的那個(gè)三角形貼上了一張磚墻圖片:


image
  • 為了能夠把紋理映射(Map)到三角形上拍皮,我們需要指定三角形的每個(gè)頂點(diǎn)各自對應(yīng)紋理的哪個(gè)部分。這樣每個(gè)頂點(diǎn)就會(huì)關(guān)聯(lián)著一個(gè)紋理坐標(biāo)(Texture Coordinate)跑杭,用來標(biāo)明該從紋理圖像的哪個(gè)部分采樣(譯注:采集片段顏色)铆帽。之后在圖形的其它片段上進(jìn)行片段插值(Fragment Interpolation)。
  • 紋理坐標(biāo)在x和y軸上德谅,范圍為0到1之間(注意我們使用的是2D紋理圖像)爹橱。使用紋理坐標(biāo)獲取紋理顏色叫做采樣(Sampling)。紋理坐標(biāo)起始于(0, 0)窄做,也就是紋理圖片的左下角愧驱,終始于(1, 1),即紋理圖片的右上角椭盏。下面的圖片展示了我們是如何把紋理坐標(biāo)映射到三角形上的组砚。

[站外圖片上傳中...(image-83db6c-1558599009476)]

  • 我們?yōu)槿切沃付?個(gè)紋理坐標(biāo)點(diǎn)。如上圖所示掏颊,我們希望三角形的左下角對應(yīng)紋理的左下角糟红,因此我們把三角形左下角頂點(diǎn)的紋理坐標(biāo)設(shè)置為(0, 0);三角形的上頂點(diǎn)對應(yīng)于圖片的上中位置所以我們把它的紋理坐標(biāo)設(shè)置為(0.5, 1.0)乌叶;同理右下方的頂點(diǎn)設(shè)置為(1, 0)改化。我們只要給頂點(diǎn)著色器傳遞這三個(gè)紋理坐標(biāo)就行了,接下來它們會(huì)被傳片段著色器中枉昏,它會(huì)為每個(gè)片段進(jìn)行紋理坐標(biāo)的插值陈肛。

紋理坐標(biāo)看起來就像這樣:

float texCoords[] = {
   0.0f, 0.0f, // 左下角
   1.0f, 0.0f, // 右下角
   0.5f, 1.0f // 上中
};

對紋理采樣的解釋非常寬松,它可以采用幾種不同的插值方式兄裂。所以我們需要自己告訴OpenGL該怎樣對紋理采樣句旱。

紋理環(huán)繞方式

紋理坐標(biāo)的范圍通常是從(0, 0)到(1, 1)阳藻,那如果我們把紋理坐標(biāo)設(shè)置在范圍之外會(huì)發(fā)生什么?OpenGL默認(rèn)的行為是重復(fù)這個(gè)紋理圖像(我們基本上忽略浮點(diǎn)紋理坐標(biāo)的整數(shù)部分)谈撒,但OpenGL提供了更多的選擇:

環(huán)繞方式 描述
GL_REPEAT 對紋理的默認(rèn)行為腥泥。重復(fù)紋理圖像。
GL_MIRRORED_REPEAT 和GL_REPEAT一樣啃匿,但每次重復(fù)圖片是鏡像放置的蛔外。
GL_CLAMP_TO_EDGE 紋理坐標(biāo)會(huì)被約束在0到1之間,超出的部分會(huì)重復(fù)紋理坐標(biāo)的邊緣溯乒,產(chǎn)生一種邊緣被拉伸的效果夹厌。
GL_CLAMP_TO_BORDER 超出的坐標(biāo)為用戶指定的邊緣顏色。

當(dāng)紋理坐標(biāo)超出默認(rèn)范圍時(shí)裆悄,每個(gè)選項(xiàng)都有不同的視覺效果輸出矛纹。我們來看看這些紋理圖像的例子:


image

前面提到的每個(gè)選項(xiàng)都可以使用glTexParameter*函數(shù)對單獨(dú)的一個(gè)坐標(biāo)軸設(shè)置(s、t(如果是使用3D紋理那么還有一個(gè)r)它們和x光稼、y或南、z是等價(jià)的):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

第一個(gè)參數(shù)指定了紋理目標(biāo);我們使用的是2D紋理艾君,因此紋理目標(biāo)是GL_TEXTURE_2D采够。第二個(gè)參數(shù)需要我們指定設(shè)置的選項(xiàng)與應(yīng)用的紋理軸。我們打算配置的是WRAP選項(xiàng)冰垄,并且指定S和T軸蹬癌。最后一個(gè)參數(shù)需要我們傳遞一個(gè)環(huán)繞方式(Wrapping),在這個(gè)例子中OpenGL會(huì)給當(dāng)前激活的紋理設(shè)定紋理環(huán)繞方式為GL_MIRRORED_REPEAT播演。

如果我們選擇GL_CLAMP_TO_BORDER選項(xiàng)冀瓦,我們還需要指定一個(gè)邊緣的顏色伴奥。這需要使用glTexParameter函數(shù)的fv后綴形式写烤,用GL_TEXTURE_BORDER_COLOR作為它的選項(xiàng),并且傳遞一個(gè)float數(shù)組作為邊緣的顏色值:

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

紋理過濾

紋理坐標(biāo)不依賴于分辨率(Resolution)拾徙,它可以是任意浮點(diǎn)值洲炊,所以O(shè)penGL需要知道怎樣將紋理像素(Texture Pixel,也叫Texel尼啡,譯注1)映射到紋理坐標(biāo)暂衡。當(dāng)你有一個(gè)很大的物體但是紋理的分辨率很低的時(shí)候這就變得很重要了。你可能已經(jīng)猜到了崖瞭,OpenGL也有對于紋理過濾(Texture Filtering)的選項(xiàng)狂巢。紋理過濾有很多個(gè)選項(xiàng),但是現(xiàn)在我們只討論最重要的兩種:GL_NEARESTGL_LINEAR书聚。

注意:Texture Pixel也叫Texel唧领,你可以想象你打開一張.jpg格式圖片藻雌,不斷放大你會(huì)發(fā)現(xiàn)它是由無數(shù)像素點(diǎn)組成的,這個(gè)點(diǎn)就是紋理像素斩个;注意不要和紋理坐標(biāo)搞混胯杭,紋理坐標(biāo)是你給模型頂點(diǎn)設(shè)置的那個(gè)數(shù)組,OpenGL以這個(gè)頂點(diǎn)的紋理坐標(biāo)數(shù)據(jù)去查找紋理圖像上的像素受啥,然后進(jìn)行采樣提取紋理像素的顏色做个。

GL_NEAREST(也叫鄰近過濾,Nearest Neighbor Filtering)是OpenGL默認(rèn)的紋理過濾方式滚局。當(dāng)設(shè)置為GL_NEAREST的時(shí)候居暖,OpenGL會(huì)選擇中心點(diǎn)最接近紋理坐標(biāo)的那個(gè)像素。下圖中你可以看到四個(gè)像素核畴,加號代表紋理坐標(biāo)膝但。左上角那個(gè)紋理像素的中心距離紋理坐標(biāo)最近,所以它會(huì)被選擇為樣本顏色:

image

GL_LINEAR(也叫線性過濾谤草,(Bi)linear Filtering)它會(huì)基于紋理坐標(biāo)附近的紋理像素跟束,計(jì)算出一個(gè)插值,近似出這些紋理像素之間的顏色丑孩。一個(gè)紋理像素的中心距離紋理坐標(biāo)越近冀宴,那么這個(gè)紋理像素的顏色對最終的樣本顏色的貢獻(xiàn)越大。下圖中你可以看到返回的顏色是鄰近像素的混合色:
image

那么這兩種紋理過濾方式有怎樣的視覺效果呢温学?讓我們看看在一個(gè)很大的物體上應(yīng)用一張低分辨率的紋理會(huì)發(fā)生什么吧(紋理被放大了略贮,每個(gè)紋理像素都能看到):
[站外圖片上傳中...(image-b9872f-1558599009476)]

GL_NEAREST產(chǎn)生了顆粒狀的圖案,我們能夠清晰看到組成紋理的像素仗岖,而GL_LINEAR能夠產(chǎn)生更平滑的圖案逃延,很難看出單個(gè)的紋理像素。GL_LINEAR可以產(chǎn)生更真實(shí)的輸出轧拄,但有些開發(fā)者更喜歡8-bit風(fēng)格揽祥,所以他們會(huì)用GL_NEAREST選項(xiàng)。

當(dāng)進(jìn)行放大(Magnify)和縮小(Minify)操作的時(shí)候可以設(shè)置紋理過濾的選項(xiàng)檩电,比如你可以在紋理被縮小的時(shí)候使用鄰近過濾拄丰,被放大時(shí)使用線性過濾。我們需要使用glTexParameter*函數(shù)為放大和縮小指定過濾方式俐末。這段代碼看起來會(huì)和紋理環(huán)繞方式的設(shè)置很相似:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多級漸遠(yuǎn)紋理

  • 想象一下料按,假設(shè)我們有一個(gè)包含著上千物體的大房間,每個(gè)物體上都有紋理卓箫。有些物體會(huì)很遠(yuǎn)载矿,但其紋理會(huì)擁有與近處物體同樣高的分辨率。由于遠(yuǎn)處的物體可能只產(chǎn)生很少的片段烹卒,OpenGL從高分辨率紋理中為這些片段獲取正確的顏色值就很困難闷盔,因?yàn)樗枰獙σ粋€(gè)跨過紋理很大部分的片段只拾取一個(gè)紋理顏色魂挂。在小物體上這會(huì)產(chǎn)生不真實(shí)的感覺,更不用說對它們使用高分辨率紋理浪費(fèi)內(nèi)存的問題了馁筐。
  • OpenGL使用一種叫做多級漸遠(yuǎn)紋理(Mipmap)的概念來解決這個(gè)問題涂召,它簡單來說就是一系列的紋理圖像,后一個(gè)紋理圖像是前一個(gè)的二分之一敏沉。多級漸遠(yuǎn)紋理背后的理念很簡單:距觀察者的距離超過一定的閾值果正,OpenGL會(huì)使用不同的多級漸遠(yuǎn)紋理,即最適合物體的距離的那個(gè)盟迟。由于距離遠(yuǎn)秋泳,解析度不高也不會(huì)被用戶注意到。同時(shí)攒菠,多級漸遠(yuǎn)紋理另一加分之處是它的性能非常好迫皱。讓我們看一下多級漸遠(yuǎn)紋理是什么樣子的:
    [站外圖片上傳中...(image-3381c5-1558599009476)]

手工為每個(gè)紋理圖像創(chuàng)建一系列多級漸遠(yuǎn)紋理很麻煩,幸好OpenGL有一個(gè)glGenerateMipmaps函數(shù)辖众,在創(chuàng)建完一個(gè)紋理后調(diào)用它OpenGL就會(huì)承擔(dān)接下來的所有工作了卓起。后面的教程中你會(huì)看到該如何使用它。

在渲染中切換多級漸遠(yuǎn)紋理級別(Level)時(shí)凹炸,OpenGL在兩個(gè)不同級別的多級漸遠(yuǎn)紋理層之間會(huì)產(chǎn)生不真實(shí)的生硬邊界戏阅。就像普通的紋理過濾一樣,切換多級漸遠(yuǎn)紋理級別時(shí)你也可以在兩個(gè)不同多級漸遠(yuǎn)紋理級別之間使用NEAREST和LINEAR過濾啤它。為了指定不同多級漸遠(yuǎn)紋理級別之間的過濾方式奕筐,你可以使用下面四個(gè)選項(xiàng)中的一個(gè)代替原有的過濾方式:

過濾方式 描述
GL_NEAREST_MIPMAP_NEAREST 使用最鄰近的多級漸遠(yuǎn)紋理來匹配像素大小,并使用鄰近插值進(jìn)行紋理采樣
GL_LINEAR_MIPMAP_NEAREST 使用最鄰近的多級漸遠(yuǎn)紋理級別变骡,并使用線性插值進(jìn)行采樣
GL_NEAREST_MIPMAP_LINEAR 在兩個(gè)最匹配像素大小的多級漸遠(yuǎn)紋理之間進(jìn)行線性插值离赫,使用鄰近插值進(jìn)行采樣
GL_LINEAR_MIPMAP_LINEAR 在兩個(gè)鄰近的多級漸遠(yuǎn)紋理之間使用線性插值,并使用線性插值進(jìn)行采樣

就像紋理過濾一樣塌碌,我們可以使用glTexParameteri將過濾方式設(shè)置為前面四種提到的方法之一:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

一個(gè)常見的錯(cuò)誤是渊胸,將放大過濾的選項(xiàng)設(shè)置為多級漸遠(yuǎn)紋理過濾選項(xiàng)之一。這樣沒有任何效果誊爹,因?yàn)槎嗉墲u遠(yuǎn)紋理主要是使用在紋理被縮小的情況下的:紋理放大不會(huì)使用多級漸遠(yuǎn)紋理蹬刷,為放大過濾設(shè)置多級漸遠(yuǎn)紋理的選項(xiàng)會(huì)產(chǎn)生一個(gè)GL_INVALID_ENUM錯(cuò)誤代碼瓢捉。

--
總結(jié):OpenGL中紋理的相關(guān)知識很重要频丘,一定要好好理解。

--
系列連載

OpenGL入門(一)-- 圖形API簡介與作用
OpenGL入門(二)-- 快速了解OpenGL下的專業(yè)名詞
OpenGL入門(三)-- OpenGL坐標(biāo)系解析與坐標(biāo)變換
OpenGL入門(四)-- OpenGL坐標(biāo)系與坐標(biāo)變換
OpenGL入門(五)-- OpenGL渲染流程圖解析
OpenGL入門(六)-- OpenGL 固定存儲著色器的理解使用
OpenGL入門(七)-- 圖形圖像渲染中的深度緩沖區(qū)
OpenGL入門(八)-- OpenGL向量和矩陣簡介
OpenGL入門(九)-- OpenGL 紋理簡單介紹

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泡态,一起剝皮案震驚了整個(gè)濱河市搂漠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌某弦,老刑警劉巖桐汤,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件而克,死亡現(xiàn)場離奇詭異,居然都是意外死亡怔毛,警方通過查閱死者的電腦和手機(jī)员萍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拣度,“玉大人碎绎,你說我怎么就攤上這事】构” “怎么了筋帖?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冤馏。 經(jīng)常有香客問我日麸,道長,這世上最難降的妖魔是什么逮光? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任代箭,我火速辦了婚禮,結(jié)果婚禮上涕刚,老公的妹妹穿的比我還像新娘梢卸。我一直安慰自己,他們只是感情好副女,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布蛤高。 她就那樣靜靜地躺著,像睡著了一般碑幅。 火紅的嫁衣襯著肌膚如雪戴陡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天沟涨,我揣著相機(jī)與錄音恤批,去河邊找鬼。 笑死裹赴,一個(gè)胖子當(dāng)著我的面吹牛喜庞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棋返,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼延都,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了睛竣?” 一聲冷哼從身側(cè)響起彩倚,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤铐望,失蹤者是張志新(化名)和其女友劉穎灿椅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體与境,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年猖吴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摔刁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡海蔽,死狀恐怖簸搞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情准潭,我是刑警寧澤趁俊,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站刑然,受9級特大地震影響寺擂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泼掠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一怔软、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧择镇,春花似錦挡逼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吝梅,卻和暖如春虱疏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苏携。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工做瞪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人右冻。 一個(gè)月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓装蓬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纱扭。 傳聞我的和親對象是個(gè)殘疾皇子牍帚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評論 2 359