只可以繪制純色的模型是不夠的,為了呈現出更真實的模型,我們還需要通過紋理貼圖給模型進行上色。
丟失上下文
GPU作為一種公用資源,是會被多個進程同時使用的,在資源不足的情況下(比如PC或手機系統進入休眠狀態前或被喚醒后),我們持有的上下文會出現丟失的情況,為了保證程序運行的健壯性,我們必須在丟失上下文之后做出處理。
Canvas為我們提供了兩個事件來監聽,上下文的丟失和恢復,具體使用看下面的代碼:
1 var canvas = document.getElementById("myGLCanvas"); 2 // 監聽上下文丟失的事件 3 canvas.addEventListener("webglcontextlost", function (event) { 4 // 取消默認行為 5 event.preventDefault(); 6 // 停止繼續繪圖的代碼 7 }, false); 8 // 監聽上下文恢復的事件 9 canvas.addEventListener("webglcontextrestored", function () { 10 // 重新初始化的代碼 11 // 需要注意的是 Canvas 通過 getContext 方法獲得的上下文對象不需要重新獲取, 還可以繼續使用之前獲取的上下文對象 12 // 開始繼續繪圖的代碼 13 }, false);
模擬丟失上下文
我們要測試丟失上下文的處理代碼是否正常,就需要觸發丟失上下文,我們可以使用下面的js庫來模擬上下文的丟失:
https://github.com/KhronosGroup/WebGLDeveloperTools
可以參考其目錄下的src\debug\lost-context-simulator-test.html示例來使用。
2D映射和立方體映射
我們需要將2D圖片貼到3D模型上,需要使用到2D的圖片,采用UV坐標來確定3D的面上的一個點可以對應2D圖片上的一個像素或多個像素(采樣),下面是uv坐標的坐標系:

(s對應u、t對應v),范圍[0-1]。
立方體映射,是一個包含了6個2D圖片的映射,一般用來實現環境映射,或者實現環境反射,下面的示例可以很好的展現環境反射的應用:
https://threejs.org/examples/#webgl_materials_envmaps
另外立方體映射還常用於創建天空盒(SkyBox)。
紋理大小
我們提交到GPU的圖片尺寸的高和寬必須是2的n次方,即(2、4、8、16、32、64、128、256...),不過在OpenGL ES 2.0和WebGL中,我們也可以使用高寬非2的n次方的圖片,即NPOT(Non Power Of Two);
如果我們使用了非2的n次方的圖片,會有下面的一些限制:
- 不能使用MipMap映射;
- 在着色器中采樣紋理貼圖時:紋理過濾方式只能用最近點或線性, 不能使用重復模式。
具體請看:https://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences
關於y軸翻轉
我們先看看DOM里的Image對象的坐標系和WebGL紋理的坐標系的區別:

可以發現,兩個坐標系的y軸剛好是相反的,所以為了使坐標系一致,我們需要使用下面的代碼來翻轉y軸:
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
紋理過濾
我們的紋理圖片和將要渲染的區域尺寸是不一定完全一致的,當紋理小於渲染區域時需要紋理伸展,當紋理大於渲染區域時需要紋理收縮;
MipMap
當紋理進行伸展過大和收縮過大時,會出現模糊和鋸齒,為了解決這個問題,我們可以使用多套尺寸的紋理,來對應不同尺寸的渲染區域,GPU會根據渲染區域的大小自動選擇;
優點
- 模型無論是遠離還是離攝像機較近時,顯示都會比較自然;
- 渲染效率更高;
缺點
- 內存使用會增大為單張圖片的1/3;
創建MipMap的方法
- 提交紋理之后,調用gl.generateMipmap方法WebGL會自動生成指定紋理的MipMap;
- 通過外部工具,直接將所有的MipMap生成好之后,手動進行提交,該方法一般用於比較特殊的情況,比如不同級別的MipMap紋理圖像不一致的情況;
紋理坐標包裝
- GL_REPEAT: 超出紋理范圍的坐標整數部分被忽略,形成重復效果。
- GL_MIRRORED_REPEAT: 超出紋理范圍的坐標整數部分被忽略,但當整數部分為奇數時進行取反,形成鏡像效果。
- GL_CLAMP_TO_EDGE:超出紋理范圍的坐標被截取成0和1,形成紋理邊緣延伸的效果。
activeTexture和bindTexture
gl.activeTexture
激活當前的操作貼圖,指定后續代碼操作的貼圖是哪一個,參數是枚舉gl.TEXTURE0到gl.TEXTURE7(最大值請查看gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS,最少是8);
后面會調用bindTexture來綁定當前的操作貼圖,如果沒有調用activeTexture就調用了bindTexture,則默認激活0號紋理單元(可以理解為默認調用了gl.activeTexture(gl.TEXTURE0)代碼);
gl.bindTexture
綁定指定紋理到activeTexture激活的紋理單元中,同時可以指定該紋理的類型;
更多詳細信息可以參考這里:https://www.jianshu.com/p/1829b4acc58d
示例
https://hammerc.github.io/dou3d-ts/learning/learningNotes/lesson_4/index.html
