在使用Three.js 開發網頁3D遊戲的過程中發現,部分材質貼圖在遠距離的時候會產生模糊(如下圖),在此寫個解決文章留作紀念,也供其他開發者參考。
方法1:Minification Filters
在Three.js 中,我們可以對Texture 物件修改兩項Texture Constants,例如:
var ObjectTexture = new THREE.TextureLoader(LoadingManager).load("textures/Billboard.png"); ObjectTexture.minFilter = THREE.NearestFilter; ObjectTexture.magFilter = THREE.LinearFilter;
其中minFilter (Minification Filters) 指的是當貼圖3D顯示小於實際貼圖大小時(物件較遠或物件較小),所套用的縮圖方式;magFilter (Magnification Filters) 指的是當貼圖3D顯示大於實際貼圖大小時(物件非常近或物件很大) 所套用的放大方式。
magFilter 共有兩種參數:
- THREE.LinearFilter
預設值,以最近的四個座標點做加權平均,如果有設定wrapS、wrapT,也會從其他重複的部分做精確的取樣 - THREE.NearestFilter
以最靠近的座標點取樣
minFilter 比較多選擇,共有以下六種參數:
- THREE.NearestFilter
以最靠近的座標點取樣 - THREE.NearestMipMapNearestFilter
採用最接近顯示大小的一個mipmap,以mipmap 最靠近的座標點取樣 (等同於做NearestFilter) - THREE.NearestMipMapLinearFilter
採用最接近顯示大小的兩個mipmap,兩個mipmap 各別最靠近的座標點取樣 (等同於各別做NearestFilter),最後這兩個值再加權平均 - THREE.LinearFilter
以最近的四個座標點做加權平均,如果有設定wrapS、wrapT,也會從其他重複的部分做精確的取樣 - THREE.LinearMipMapNearestFilter
採用最接近顯示大小的一個mipmap,以mipmap 最近的四個座標點做加權平均 (等同於做LinearFilter) - THREE.LinearMipMapLinearFilter
預設值,採用最接近顯示大小的兩個mipmap,兩個mipmap 各別最近的四個座標點做加權平均(等同於各別做LinearFilter),最後再將兩個值再加權平均
由上方圖片所示,NearestFilter 會產生鋸齒;NearestMipMapNearestFilter 、NearestMipMapLinearFilter 以及LinearMipMapNearestFilter 雖然消除鋸齒但是貼圖破碎無法辨識文字。
由此得知只要經過NearestFilter 的取樣都會產生模糊甚至貼圖破碎,其中以LinearFilter 最清晰,但是會產生輕微鋸齒,若在Renderer 開啟抗鋸齒(antialias) 的環境中可能會看起來有些突兀。
方法2:Renderer Anisotropy
我們可以設定貼圖的mipmap 採樣數來解決模糊問題。
var Renderer = new THREE.WebGLRenderer(); var maxAnisotropy = Renderer.capabilities.getMaxAnisotropy(); ObjectTexture.anisotropy = maxAnisotropy;
先透過getMaxAnisotropy() 函數取得GPU所支援的採樣最大值(這個值為2的升幕),並修改貼圖的anisotropy 參數(預設為1),這個動作會影響貼圖在3D顯示時,不同距離所採用的mipmap 數量,並會影響效能。
最後結果
參考資料
- https://threejs.org/docs/#api/en/constants/Textures
- https://threejs.org/examples/webgl_materials_texture_anisotropy.html
- https://threejs.org/examples/webgl_materials_texture_filters.html