Android OpenGL ES(七)----理解紋理與紋理過濾
1.理解紋理
OpenGL中的紋理可以用來表示影象,照片,甚至由一個數學演算法生成的分形資料。每個二維的紋理都由許多小的紋理元素組成,它們是小塊的資料,類似於我們前面討論過的片段和畫素。要使用紋理,最常用的方式是直接從一個影象檔案載入資料。
每個二維紋理都有其自己的座標空間,其範圍是從一個拐角的(0,0)到另一個拐角的(1,1)。按照慣例,一個維度叫做S,而另一個稱為T。當我們想要把一個紋理應用於一個三角形或一組三角形的時候,我們要為每個頂點指定一組ST紋理座標,以便OpenGL知道需要用那個紋理的哪個部分畫到每個三角形上。這些紋理座標有時也會被稱為UV紋理座標。如圖:
圖1 OpenGL二維紋理座標
對一個OpenGL紋理來說,它沒有內在的方向性,因此我們可以使用不同的座標把它定向到任何我們喜歡的方向上。然而,大多數計算機影象都有一個預設的方向,它們通常被規定為Y軸向下,Y的值隨著向影象的底部移動而增加。只要我們記住,如果想用正確的方向觀察影象,那紋理座標就必須要考慮這點,這就不會給我們帶來任何麻煩。
在標準OpenGL ES 2.0中,紋理不必是正方形,但是每個維度都應該是2的冪(POT)。這就意味著每個維度都是這樣的一個數字,如128,256,512等。這樣規定的原因在於非POT紋理可以被使用的場合非常有限,而POT紋理使用於各種情況。
紋理的尺寸也有一個最大值,它根據不同的實現而變化,但是通常都比較大,比如2048*2048
2.理解紋理過濾
當紋理的大小被擴大或者縮小時,我們還需要使用紋理過濾明確說明會發生什麼。當我們在渲染表面上繪製一個紋理時,那個紋理的紋理元素可能無法精確地對映到OpenGL生成的片段上。有兩種情況:縮小和放大。當我們盡力把幾個紋理元素擠進一個片段時,縮小就發生了;當我們把一個紋理元素擴充套件到許多片段時,方法就發生了。針對每一種情況,我們可以配置OpenGL使用一個紋理過濾器。
首先,講述兩個基本的過濾模式:最近鄰過濾和雙線性插值。還有其他的過濾模式,以後的博文會講解。我們會使用下面的影象闡述每一種過濾模式。
最近鄰過濾
這個方式為每個片段選擇最近的紋理元素。當我們放大紋理時,它的鋸齒效果看起來相當明顯,如下圖所示。
每個紋理單元都清楚的顯示為一個小方塊。
當我們縮小紋理時,因為沒有足夠的片段用來繪製所有的紋理單元,許多細節將會丟失。
雙線性過濾
雙線性過濾使用雙線性插值平滑畫素之間的過渡,而不是為每個片段使用最近的紋理元素,OpenGL會使用四個鄰接的紋理元素,並在它們之間用一個線性插值演算法做插值,這個演算法與前面所講的平滑坐在著色一樣。我們之所以稱它為雙線性插值,是因為它是沿著兩個維度插值的。下面是使用雙線性差值放大後的影象,它採用的紋理與前面的相同。
這個紋理現在看起來比以前平滑多了。但還是有些鋸齒顯現出來,因為我們把這個紋理擴充套件得太多,但是鋸齒不像使用最近鄰過濾那麼明顯。
MIP貼圖
儘管雙線性過濾很適合處理放大,但是對於縮小到超過一定的大小時,它就不好用了。一個紋理在渲染表面所佔大小減少得越多,就會有越多的紋理元素擁擠到每一個片段上。因為OpenGL的雙線性過濾只給每個片段使用四個紋理元素,我們將會丟失很多細節。因為每一幀都要選擇不同的紋理元素,這還會引起噪音以及移動中的物體閃爍。
為了克服這些缺陷,可以使用MIP貼圖技術,它可以用來生成一組優化過的不同大小的紋理。當生成這組紋理的時候,OpenGL會使用所有的紋理元素生成每個級別的紋理,當過濾紋理時,還要確保所有的紋理元素都能被使用。在渲染時,OpenGL會根據每個片段的紋理元素數量為每個片段選擇最合適的級別。
下圖是一組MIP貼圖的紋理,把它們合併在一當個圖上是為了方便對比。
圖2 MIP貼圖的紋理
使用MIP貼圖,會佔用更多的記憶體,但是渲染也會更快,這是因為較小級別的紋理在GPU的紋理快取中佔用較少的空間。
為了更好地理解MIP貼圖是如何提高縮小情況下的質量,我們比較一下那個可愛的機器人,使用雙線性過濾把紋理元素尺寸縮小到其原來的12.5%,如下圖:
圖3 使用雙線性過濾縮小
就這種質量,可能還不如最近鄰過濾。看一下當我們加入MIP貼圖時會得到什麼。如下圖:
圖4 使用MIP貼圖縮小
隨著MIP貼圖的使用,OpenGL將選擇最合適的紋理級別,然後用優化過的紋理做雙線性插值。每個級別的紋理都是用來自所有紋理元素的資訊構建的,因此得到的圖形看起來更好些,保留了更多的細節。
三線性過濾
如果OpenGL在不同的MIP貼圖級別之間來回切換,當我們用雙線性插值來使用MIP貼圖時,在其渲染的場景中,在不同級別的MIP貼圖切換時,我們有時候能看到明顯的跳躍或者線條。我們可以切換到三線性插值,這樣,每個片段總共要使用8個紋理元素插值。這有助於消除每個MIP貼圖級別之間的過渡,並且得到一個更平滑的影象。
3.程式中紋理引數表
方法GLES20.glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_FILTER,“紋理過濾模式”);第二個引數指放大的情況。
方法GLES20.glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,“紋理過濾模式”);第二個引數指縮小的情況。
第一個引數是告訴OpenGL這應該被作為一個二維紋理對待。
表1 OpenGL紋理過濾模式
GL_NEAREST |
最近鄰過濾 |
GL_NEAREST_MIPMAP_NEAREST |
使用MIP貼圖的最近鄰過濾 |
GL_NEAREST_MIPMAP_LINEAR |
使用MIP貼圖級別之間插值的最近鄰過濾 |
GL_LINEAR |
雙線性插值 |
GL_LINEAR_MIPMAP_NEAREST |
使用MIP貼圖的雙線性插值 |
GL_LINEAR_MIPMAP_LINEAR |
三線性插值(使用MIP貼圖級別之間插值的雙線性過濾) |
表2 每種情況允許的紋理過濾模式
縮小 | GL_NEAREST GL_NEAREST_MIPMAP_NEAREST GL_NEAREST_MIPMAP_LINEAR GL_LINEAR GL_LINEAR_MIPMAP_NEAREST GL_LINEAR_MIPMAP_LINEAR |
放大 | GL_NEAREST GL_LINEAR |
下一篇紋理的應用效果圖如下: