跳至內容

雙線性過濾

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書
經過縮放的一小部分點陣圖圖像,左圖使用了最近鄰插值過濾,中間的圖用了雙線性過濾,右圖是用了立方過濾。立方插值過濾類似於雙線性插值

雙線性過濾Bilinear filtering)是進行縮放顯示的時候進行紋理平滑的一種紋理過濾方法。 在大多數情況下,紋理在螢幕上顯示的時候都不會同儲存的紋理一模一樣,沒有任何損失。正因為這樣,所以一些像素要使用紋素之間的點進行表示,在這裡我們假設紋素都是位於各個單元中心或者左上或者其它位置的點。雙線性過濾器利用像素所表示點周圍四個最近的點(紋素點)之間進行雙線性插值

公式

在下面這些方程中,uk 與 vk 是點 k 處的紋理坐標,yk 是點 k 處的顏色值。不帶下標的值表示像素點,帶有下標 0、1、2、3 的值表示從左上沿順時針方向到左下包圍像素的紋素點,帶有下標 a、b 的值分別表示像素點在點0、1的連線與點3、2連線上的投影點。由於雙線性插值方程是線性插值方程的一種特殊形式,所以我們從較簡單的線性插值方程開始分析。

假設紋理是正方形的點陣圖,並且滿足

我們進一步定義,

這樣就可以將插值方程化簡為:

代入方程,得到:

或者,

這種表示相當簡單。但是,如果圖像只進行縮放處理,而沒有旋轉、扭曲、透視或者其它處理,那麼使用獨立的方程計算並且儲存用於後面資料行計算的 yb 或者 ya 速度將更快。

範例代碼

這部分代碼假設紋理是常見的正方形,沒有Mipmap,並且只有一個通道的資料。只有一個通道的情況非常少見,幾乎所有的紋理都是彩色的,都有紅色、綠色與藍色通道,有些還有阿爾法透明通道,所以每個 y 都要進行三到四次的計算

double getBilinearFilteredPixelColor(Texture tex, double u, double v) {
  u *= tex.size;
  v *= tex.size;
  int x = floor(u);
  int y = floor(v);
  double u_ratio = u - x;
  double v_ratio = v - y;
  double u_opposite = 1 - u_ratio;
  double v_opposite = 1 - v_ratio;
  double result = (tex[x][y]   * u_opposite + tex[x+1][y]   * u_ratio) * v_opposite + 
                  (tex[x][y+1] * u_opposite + tex[x+1][y+1] * u_ratio) * v_ratio;
  return result;
}

局限

在紋理縮減到一半或者放大一倍的範圍內,雙線性過濾都能夠有非常好的精度。這也就是說,如果紋理在每個方向都有 256 個像素,那麼將它縮減到 128 以下或者放大到 512 以上的時候,由於會丟掉太多的像素或者進行了過多的平滑處理,紋理看起來就會很差。通常,可以在縮減的過程中使用 Mipmap 來實現較好的效能;但是,在透視圖中的紋理上的經過雙線性過濾處理的兩個不同尺寸的 mipmap 之間的過渡將非常明顯。三線性過濾儘管比較複雜,但是可以使得過渡非常平滑。

為了快速說明紋理過濾中如何遺失紋素,這裡有一組用來自於 8 紋素寬紋理的數字表示的盒子的中心,它們與藍色表示的來自於 3 紋素寬的紋理表示的盒子中心的一組數字混雜在一起。紅色數字表示計算 3 紋素紋理中根本不需要的紋素。

0.0625, 0.1667, 0.1875, 0.3125, 0.4375, 0.5000, 0.5625, 0.6875, 0.8125, 0.8333, 0.9375

特殊情況

通常紋理是有限大小的,我們經常會得到坐標位於紋素坐標之外柵格之外的像素。可以用以下幾種方法來處理這種情況:

  • 旋繞紋理,這樣一行中的最後一個紋素將出現在該行第一個紋素的前面,一列中的最後一個紋素將出現在該列第一個紋素前面。在紋理平鋪的時候這種方法可以得到最好的效果。
  • 紋理之外的區域使用一種顏色。這可能並不是一個非常了不起的想法,但是如果紋理要放到固體或者透明背景上,那麼就可以使用這種方法。
  • 無限重複邊界紋素。如果所設計的紋理不是要重複使用的話,這種方法可以很好地工作。

參見