線性插值之雙線性插值與三線性插值



轉自:http://www.legalsoft.com.cn/docs/docs/17/267.htmlhtml

當你作紋理映射的時候,是否常常會注意到屏幕上顯示出的那些明顯鋸齒,並且你用的紋理像素化得太明顯了?如今,咱們將談論如何來解決這個問題,而咱們使用的方法就是對你的紋理進行濾波。下面咱們將介紹幾種經常使用的濾波方法,最後再詳細介紹雙線性插值濾波的具體實現。
Bi-linear Interpolation
  雙線性插值是經過對紋理中的相鄰像素進行處理來平滑掉屏幕輸出像素間的鋸齒的。使用雙線性插值會使屏幕輸出的圖像顯得更平滑。下面先來看看它的基本計算公式。
double texture[N][M]; // x = [0, N), y = [0, M)
double xReal; // xReal = [0, N - 1]
double yReal; // yReal = [0, M - 1]
int x0 = int(xReal), y0 = int(yReal);
double dx = xReal - x0, dy = yReal - y0,
omdx = 1 - dx, omdy = 1 - dy;
double bilinear = omdx * omdy * texture[x0][y0] +
omdx * dy * texture[x0][y0+1] +
dx * omdy * texture[x0+1][y0] +
dx * dy * texture[x0+1][y0+1];
  觀察這段公式,你會看出,咱們頗有效地使用了紋理座標的小數部分來對四個紋理中的相鄰像素進行插值。咱們按對應像素的距離來決定各個像素所佔的權重。也就是說,當紋理的U座標的小數部分增長時,左邊相鄰像素的權重就會減小,減小出來的權重會增長右邊的相鄰像素上去。對垂直方向的V座標的狀況也同此相似。
  在實際應用中,直接按這段公式來計算顯然會很慢,你能夠用定點整數和查表法來取消浮點和整型的混合運算以及去掉乘法。(提示:針對A、B兩種顏色的混合創建 x*A+(1-x)*B 的結果表)
Mip-Mapping
  我第一次看到Mip-mapping技術是在遊戲QUAKE裏,而如今這種技術早已經是隨處可見了。這種技術是由Williams在1983年發明的,"Mip"這個名稱起源於"multum in parvo",大概就是在一小塊地方有不少東西的意思。
  具體說來,Mip-Mapping的思想就是構建一套紋理,總共須要大約1.3倍的內存。其中,每塊子紋理是經過對父紋理過濾而獲得,它的長和寬都是其父紋理的1/2,其面積爲父紋理的1/4。接下來,在應用的時候,你根據距離選取最合適的一塊來進行映射,實踐證實,這種技術雖然簡單,但對提升紋理映射的質量確實很是有效。
  經過Mip-Mapping,能夠爲較小的多邊形映射上面積較小的紋理,這對減小紋理的擾動大有好處。舉個例子,你有一塊256x256大小的紋理,當它開始向遠離觀察者的方向開始移動時,你會看到它開始閃爍和顫動。這種現象的出現是由於咱們把一大塊紋理映射到一個很小的區域而引發的。你可能在上一幀時,畫的是紋理中(50,20)處的像素,到了下一幀,卻畫的是紋理中(60,30)處的像素。若是這兩個像素相差很大,你就會觀察到前面所說的現象了。總的來講,這種劇烈的紋理座標的變化,會損害圖像的品質,而且影響CACHE的效率,而Mip-Mapping無疑是解決這個問題的好辦法。
Tri-linear Interpolation
  在介紹了雙線性插值和Mip-Mapping之後,該來說講三線性插值了。其實三線性插值也很簡單,它就是前兩種技術的結合。它在對Mip-Mapping的每塊紋理作雙線性插值的同時,還要對Mip-Mapping中相鄰的兩塊紋理按距離再作一次插值。既算出較大的一塊紋理上的某點雙線性插值像素值和較小的一塊紋理上的某點雙線性插值像素值,再按目標同兩塊紋理的距離作一次相似的插值。
  使用三線性插值,能夠消除Mip-Mapping裏紋理切換(既上一幀時用的是某個大小的一塊紋理,而下一幀時又換了一塊的狀況)時的忽然變化,從而能夠提供很平暢的高質圖像輸出。
  同前兩種技術相比,三線性插值的運算量很是大,目前只能依靠硬件來實現。
雙線性插值紋理映射的實現
  下面,咱們經過一段描述性代碼來簡單看看雙線性插值紋理映射是如何實現的。
此處略去各類初始化代碼,直接觀察咱們最關心的部分
其中:U和V是16.16格式的定點整數
du和dv是浮點數
du = (U & 0xFFFF) / 65536.0
dv = (V & 0xFFFF) / 65536.0
invdu = 1.0 - du
invdv = 1.0 - dv
// 根據到相鄰四個像素的距離計算各自的權重
Weight1 = invdu*invdv
Weight2 = invdu*dv
Weight3 = du*invdv
Weight4 = du*dv
// 求得各個像素的RGB顏色份量
r00 = Texture[V >> 16][U >> 16].Red
g00 = Texture[V >> 16][U >> 16].Green
b00 = Texture[V >> 16][U >> 16].Blue
r01 = Texture[(V >> 16) + 1][U >> 16].Red
g01 = Texture[(V >> 16) + 1][U >> 16].Green
b01 = Texture[(V >> 16) + 1][U >> 16].Blue
r10 = Texture[V >> 16][(U >> 16) + 1].Red
g10 = Texture[V >> 16][(U >> 16) + 1].Green
b10 = Texture[V >> 16][(U >> 16) + 1].Blue
r11 = Texture[(V >> 16) + 1][(U >> 16) + 1].Red
g11 = Texture[(V >> 16) + 1][(U >> 16) + 1].Green
b11 = Texture[(V >> 16) + 1][(U >> 16) + 1].Blue
// 按權重混合RGB顏色份量
Red = Weight1*r00 + Weight2*r01 + Weight3*r10 + Weight4*r11
Green = Weight1*g00 + Weight2*g01 + Weight3*g10 + Weight4*g11
Blue = Weight1*b00 + Weight2*b01 + Weight3*b10 + Weight4*b11
// 按最後求得的RGB顏色份量畫點
PutPixel(X, Y, Pack(Red, Green, Blue))
  這段代碼顯然未經優化(起碼不要去用那個PutPixel),若是你程序功力不夠,可能會沒法達到理想的優化目標,這時你能夠直接使用硬件去實現(新的3D硬件都能支援這些功能)。但我相信你在理解了雙線性插值濾波的思想之後,必定能觸類旁通,利用它爲你的遊戲圖像更添魅力。 app

相關文章
相關標籤/搜索