OpenGL Android課程六:介紹紋理過濾

奏響曲發表於2019-02-24

翻譯文

原文標題:Android Lesson Six: An Introduction to Texture Filtering 原文連結:www.learnopengles.com/android-les…


介紹紋理過濾

這節課,我們將介紹基本紋理過濾的不同型別和怎樣使用它們,
包括最鄰近(nearest-neighbour)過濾,雙線性(bilinear)過濾
和使用mipmap的三線性(trilinear)過濾

你將學習如何使紋理看起來更平滑,以及平滑帶來的缺點。
[這兒有旋轉物體][]的不同方式,本課使用了其中一。
screenshot

前提條件

強烈建議您先閱讀OpenGL Android課程四:介紹紋理基礎,理解紋理對映在OpenGL中的基本使用。

什麼是紋理過濾?

OpenGLES中的紋理由元素陣列組成,被稱為紋素(texels),其中包含顏色和alpha值。這與顯示器相對應,顯示器由一堆畫素組成,並在每個點顯示不同的顏色。在OpenGL中紋理被用在三角形上並繪製到螢幕,因此這些紋理能繪製出各種各樣的尺寸和方向。OpenGL中的紋理過濾選項告訴它如何根據具體情況將紋理畫素過濾到裝置的畫素上。

有三種情況:

  • 每個紋素對映到多個畫素,這被稱為放大(magnification)
  • 每個紋素精確的對映到一個畫素,過濾不適合這種情況
  • 每個紋素對映少於一個畫素,這被稱為縮小(minification)

OpenGL允許我們為放大和縮小分配過濾器,並允許我們使用最鄰近、雙線性和三線性過濾。我們將在下面解釋這些意思。

放大和縮小

這裡是放大和縮小的最鄰近渲染的視覺化,當您用USB連線你的Android裝置時使用這個可愛的Android顯示成功連線。

cute android

放大

magnification android

正如您所見,紋素現在很容易看到,因為當前一個紋素覆蓋了很多畫素展示出來。

縮小

minification android

隨著縮小,許多紋素不能渲染到有限的畫素上,許多細節將會丟失。

紋理過濾模式

雙線性插值(Bilinear interpolation)

當紋素值之間沒有插值時,在放大示例中,紋理的紋素清晰可見為大正方形。當使用最鄰近方式時,畫素將會分配到最鄰近的畫素。

通過切換到雙線性插值,渲染質量顯著提高。這些值將會在鄰近的四個畫素之間線性插值,而不是將一組畫素分配給鄰近相同的紋素值。每個畫素被平滑化,使得最後的圖片看起來也更平滑:

smoother android

一些塊效果仍然很明顯,但是這個圖片看起來比之前更加平滑。那些在3D加速卡出現前玩過3D遊戲的人將會記得軟體渲染遊戲和硬體加速遊戲之間的特性:軟體渲染遊戲根本沒有進行預計算處理,所以一切都顯示得塊狀和鋸齒狀。一旦人們開始使用圖形加速,這些東西都將變得平滑。

smooth

雙線性插值大多使用在放大。它也能使用在縮小,但是超過某個度,我們將會遇到同樣的問題,我們在嘗試將太多的紋素放到相同的畫素上。OpenGL僅使用最多4個紋素渲染一個畫素,因此許多資訊仍然會丟失。

如果我們看應用了雙線性插值的紋理,當我們在遠處看它移動時看起來會很嘈雜,因為每幀都會選擇不同的紋素。

紋理對映(Mipmapping)

我們如何才能在縮小紋理時不引用嘈雜並使用上所有紋素呢?我們可以生成一組優化後的不同尺寸的紋理,然後在我們執行的時候使用它們。由於這些紋理已預先生成,它們能使用更多高昂的技術去過濾所有紋素,並且在執行時OpenGL會根據紋理在螢幕上的最終大小選擇最合適的層。

textures set

生成的圖片可以具有更多細節,更少噪點,並且整體上看起來更好。儘管需要更多的記憶體,但渲染速度也會更快,因為較小的層級能更容易儲存在GPU的紋理快取中。讓我們來仔細研究一下原尺寸的1/8倍的圖片,在使用了雙線性過濾使用紋理對映和雙線性過濾沒有使用對映。為了清楚圖片已被擴大:

雙線性過濾沒有mipmap

without mipmaps

雙線性過濾+mipmap

with mipmaps

使用mipmap的版本擁有更多細節,由於影象預處理到單獨的層級,所有紋素最終都會在最終的影象中使用。

三線性過濾(Trilinear filtering)

當使用雙線性過濾的mipmap時,有時在渲染場景中可以看到明顯的跳躍或線,由於OpenGL在紋理的不同mipmap層級之間切換。比較不同的OpenGL紋理的過濾模式將在下面進一步指出。

三線性插值通過在不同mipmap層級之間插值來解決這個問題,這樣總共8個紋素將用於插值得到最終的畫素值,使得影象更平滑。

OpenGL 紋理過濾模式

OpenGL有兩個可被設定的引數:

  • GL_TEXTURE_MIN_FILTER 紋理縮小時的過濾模式
  • GL_TEXTURE_MAG_FILTER 紋理放大時的過濾模式

這些相對應於上面的縮小和放大描述。

  • GL_TEXTURE_MIN_FILTER接受以下選項:
    • GL_NEAREST
    • GL_LINEAR
    • GL_NEAREST_MIPMAP_NEAREST
    • GL_NEAREST_MIPMAP_LINEAR
    • GL_LINEAR_MIPMAP_NEAREST
    • GL_LINEAR_MIPMAP_LINEAR
  • GL_TEXTURE_MAG_FILTER接受以下選項:
    • GL_NEAREST
    • GL_LINEAR

GL_NEAREST 對應最鄰近渲染;
GL_LINEAR 對應雙線性過濾;
GL_LINEAR_MIPMAP_NEAREST 對應雙線性過濾+mipmap;
GL_LINEAR_MIPMAP_LINEAR 對應三線性過濾;
本課中將進一步介紹圖形示例和最常見選項的進一步說明。

怎樣設定紋理過濾模式

我們首先需要繫結紋理,然後我們在這個紋理上設定合適的過濾引數:

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureHandle);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, filter);
複製程式碼

怎樣生成mipmap

這真的很容易!在載入紋理到OpenGL中後,紋理仍然是繫結的,我們可以簡單的呼叫:

GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
複製程式碼

它將為我們生成所有的mipmap層級,並且這些層級會根據紋理過濾自動使用。

它看起來怎麼樣?

以下是可用的最常見的組合的螢幕截圖,當你看到它運動中時,效果更加引人注目,因此我建議下載這個App並試一試。

最鄰近渲染

這個模式讓人想起舊版3D遊戲軟體的渲染。

GL_TEXTURE_MIN_FILTER = GL_NEAREST
GL_TEXTURE_MAG_FILTER = GL_NEAREST
複製程式碼

nearest nearest

雙線性過濾,mipmap

許多支援3D加速的首批遊戲都使用此模式,這是今天在Android手機上平滑紋理的有效方式。

GL_TEXTURE_MIN_FILTER = GL_LINEAR_MIPMAP_NEAREST
GL_TEXTURE_MAG_FILTER = GL_LINEAR
複製程式碼

linear mipmap

靜態圖上很難看圖問題,但是當物體運動時,您可能會注意到渲染的畫素在mipmap層級之間切換的水平條帶。

三線性過濾

此模式通過在mipmap層級之間進行插值,改進了使用mipmap的雙線性過濾的渲染質量。

GL_TEXTURE_MIN_FILTER = GL_LINEAR_MIPMAP_LINEAR
GL_TEXTURE_MAG_FILTER = GL_LINEAR
複製程式碼

trilinear

畫素在近距離和遠距離之間完全平滑;事實上,紋理現在可能在傾斜角度下顯示的過於平滑。 各向異性過濾(Anisotropic filtering)是一種更先進的技術,受到某些移動GPU的支援,可用於改善最終結果,超出三線性過濾所能提供的效果。

進一步練習

使用其他模式可以達到什麼樣的效果?例如,您何時會使用像GL_NEAREST_MIPMAP_LINEAR這樣的東西?

教程目錄

打包教材

可以在Github下載本課程原始碼:下載專案
本課的編譯版本也可以再Android市場下:google play 下載apk
“我”也編譯了個apk,方便大家下載:github download

相關文章