WebGL使用了GLSL ES(OpenGL ES)着色器語言,經過配合調用js相關的繪製接口來實現3D效果。web
採用頁面中的<canvas>元素來定義繪圖區域,canvas支持三維圖形的繪製,但它不直接提供繪圖方法,而是提供一種叫上下文(context)機制來繪製圖形。canvas
1 var canvas = document.getElementById('canvas'); 2 var gl = canvas.getContext('webgl');
具體的參數能夠參考這裏:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/getContext數組
1 // 着色器是字符串形式的glsl代碼 2 var VSHADER_SOURCE = "..."; 3 var FSHADER_SOURCE = "...";
createShader方法,須要傳遞參數指明是頂點着色器仍是片斷着色器;緩存
shaderSource方法,能夠指定着色器代碼添加到指定的Shader上;webgl
compileShader方法,能夠將附加了着色器的shader對象進行編譯;spa
createProgram方法,能夠建立一個程序對象,一個可用的WebGLProgram對象由兩個編譯事後的WebGLShader組成,即頂點着色器和片斷着色器;code
attachShader方法,能夠把一個編譯好的着色器對象添加到一個程序對象上;orm
linkProgram方法,鏈接一個程序對象,鏈接以後的程序對象可使用;對象
useProgram方法,設定一個程序對象爲當前使用的程序對象;blog
根據須要調用對應的方法進行繪製;
示例代碼點擊這裏;
繪製圖像須要向GPU傳遞多個頂點,WebGL提供了一種很方便的機制,緩衝區對象(buffer object),它是WebGL系統中的一塊內存區域,它能夠一次性向着色器傳入多個頂點的數據,而後將這些數據保存在其中,供頂點着色器使用。
繪製圖像除了要完成上面說的步驟以外,還有下面的步驟須要完成:
createBuffer方法,建立緩衝區對象;
bindBuffer方法,綁定緩衝區對象,同時代表緩衝區的類型,以下:
bufferData方法,須要代表緩衝數據的類型,同上,還要代表數據是否會變更,以下:
getAttribLocation方法,獲取指定屬性的下標;
vertexAttribPointer方法,會告知到GPU的屬性怎麼獲取傳入的數據,參數分別以下:
enableVertexAttribArray方法,就是開啓指定的變量,使緩存區對attribute變量分配生效,以便頂點着色器可以訪問緩衝區內的數據。
使用attribute定義的屬性都是能夠接收外部傳入的數據的屬性,接收到的數據能夠直接在着色器中使用;
1 attribute vec4 a_Position; 2 3 void main() { 4 gl_Position = a_Position; 5 }
drawArrays方法,用來繪製圖像,參數分別以下:
第一個參數:繪製類型
三角形 gl.TRIANGLES 一系列單獨的三角形,繪製(v0,v1,v2),(v3,v4,v5)……若是不是3的倍數,剩下的將會被忽略。
三角帶 gl.TRIANGLES_STRIP 一系列鏈接的三角形,繪製(v0,v1,v2),(v2,v1,v3),(v2,v3,v4)……以此類推,第二個是(v2,v1,v3)而不是(v1,v2,v3)是爲了保持繪製按照逆時針繪製
第二個參數:開始繪製的頂點索引
第三個參數:繪製的頂點數量
示例代碼點擊這裏;
咱們但願修改傳輸到GPU中的頂點的位置時(好比放大縮小旋轉等),須要從外部傳入一個矩陣來進行運算,外部傳入的矩陣是一個包含16個數字的帶數據類型的數組對象;
關於矩陣的相關教程能夠看這裏;
1 // 控制縮放的矩陣 2 var Sx = 1.5; Sy = 1.5; Sz = 1.0; 3 var xformMatrix = new Float32Array([ 4 Sx, 0.0, 0.0, 0.0, 5 0.0, Sy, 0.0, 0.0, 6 0.0, 0.0, Sz, 0.0, 7 0.0, 0.0, 0.0, 1.0 8 ]);
uniformMatrix4fv方法,傳輸到GPU,參數以下:
代碼以下:
1 // 獲取矩陣變量的下標 2 var u_xformMatarix = gl.getUniformLocation(shaderProgram, 'u_xformMatarix'); 3 // 提交矩陣數據到GPU 4 gl.uniformMatrix4fv(u_xformMatarix, false, xformMatrix);
1 attribute vec4 a_Position; 2 uniform mat4 u_xformMatarix; 3 4 void main() { 5 // 設置座標 6 gl_Position = u_xformMatarix * a_Position; 7 }
示例代碼點擊這裏;
咱們能夠經過頂點數據向着色器添加顏色信息。
1 //頂點座標和顏色 2 var vertices = new Float32Array([ 3 0.0, 0.5, 1.0, 0.0, 0.0, 4 -0.5, -0.5, 0.0, 1.0, 0.0, 5 0.5, -0.5, 0.0, 0.0, 1.0, 6 ]);
顏色是在片斷着色器中處理的,可是頂點數據只在頂點着色器中可使用,因此咱們須要將頂點着色器中的部分數據傳遞到片斷着色器中,使用varying定義的屬性能夠將頂點着色器中的數據傳遞到片斷着色器中;
1 // 頂點着色器 2 attribute vec4 a_Position; 3 attribute vec4 a_Color; 4 // 聲明須要傳遞到片斷着色器的屬性 5 varying vec4 v_Color; 6 7 void main() { 8 // 設置座標 9 gl_Position = a_Position; 10 // 傳遞顏色數據 11 v_Color = a_Color; 12 } 13 14 15 // 片斷着色器 16 // 必須在片斷着色器中爲float指定一個默認精度 17 precision mediump float; 18 // 聲明從頂點着色器接收數據的屬性 19 varying vec4 v_Color; 20 21 void main() { 22 gl_FragColor = v_Color; 23 }
1 //獲取單個字節 2 var FSIZE = vertices.BYTES_PER_ELEMENT; 3 4 //獲取座標點 5 var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position'); 6 7 //將緩衝區對象分配給a_Position變量 8 gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0); 9 10 //鏈接a_Position變量與分配給它的緩衝區對象 11 gl.enableVertexAttribArray(a_Position); 12 13 //獲取Color座標點 14 var a_Color = gl.getAttribLocation(shaderProgram, 'a_Color'); 15 16 //將緩衝區對象分配給a_Position變量 17 gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2); 18 19 //鏈接a_Position變量與分配給它的緩衝區對象 20 gl.enableVertexAttribArray(a_Color);
示例代碼點擊這裏;
這裏咱們看看怎麼使用webgl來繪製圖片;
經過建立Image對象,能夠動態加載指定的圖片,保存這個Image對象,後期直接使用這個對象提交到GPU便可;
1 //對紋理圖像進行Y軸反轉,由於WebGL紋理座標系統的t軸(分爲t軸和s軸)的方向和圖片的座標系統Y軸方向相反。所以將Y軸進行反轉。 2 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); 3 4 //開啓0號紋理單元,WebGL經過紋理單元的機制來同時使用多個紋理,gl.TEXTURE0~gl.TEXTURE7是管理紋理圖像的8個紋理單元 5 gl.activeTexture(gl.TEXTURE0); 6 7 //向target綁定紋理對象 8 gl.bindTexture(gl.TEXTURE_2D, texture); 9 10 //配置紋理參數 11 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 12 13 //配置紋理圖像 14 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); 15 16 //獲取u_Sampler的存儲位置 17 var u_Sampler = gl.getUniformLocation(shaderProgram, 'u_Sampler'); 18 19 //將0號紋理圖像傳遞給着色器 20 gl.uniform1i(u_Sampler, 0); 21 22 // 清空 <canvas> 23 gl.clear(gl.COLOR_BUFFER_BIT); 24 25 // 繪製矩形 26 gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
Webgl中,是使用UV座標來標記圖像的採樣點,UV的詳情點擊這裏;
1 // 頂點着色器 2 attribute vec4 a_Position; 3 attribute vec2 a_TextCoord; 4 varying vec2 v_TexCoord; 5 6 void main() { 7 // 設置座標 8 gl_Position = a_Position; 9 // 傳遞uv座標 10 v_TexCoord = a_TextCoord; 11 } 12 13 // 片斷着色器 14 precision mediump float; 15 // 聲明採樣器 16 uniform sampler2D u_Sampler; 17 varying vec2 v_TexCoord; 18 19 void main() { 20 // 設置顏色,經過採樣得到指定座標的顏色 21 gl_FragColor = texture2D(u_Sampler, v_TexCoord); 22 }
示例代碼點擊這裏;
咱們以前是提交頂點數據,而後經過drawArrays來繪製三角形,咱們知道一個矩形是由兩個三角形組合而成的,因此能夠用下面的兩種方式來進行繪製:
1 // 3個頂點爲一組進行繪製,繪製一個矩形須要6個點 2 gl.drawArrays(gl.TRIANGLES, 0, 6); 3 4 // 傳入4個頂點,按照(v0,v1,v2),(v2,v1,v3),(v2,v3,v4)...的順序進行繪製 5 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
這裏,第一種方法,會致使大量的重複頂點數據存在,第二種方法,雖然能夠節省頂點數據,可是是規定好的順序,不夠自由;
爲了解決這個問題,WebGL還引入了另一種繪製方法drawElements,這種方法須要傳遞兩種數據,一種是頂點數據,一種是索引數據;
顧名思義,頂點數據定義一堆不一樣的頂點,而索引數據定義這些頂點的繪製順序;
1 //定義索引數組 2 var indexDatas = 3 [ 4 0, 1, 2, 5 2, 1, 3, 6 ];
1 //建立緩衝區對象 2 indexBuffer = gl.createBuffer(); 3 //將緩衝區對象綁定到目標,並設定該緩衝區是索引緩衝 4 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 5 //向緩衝區寫入數據 6 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexDatas), gl.STATIC_DRAW);
不須要作任何的修改;
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
示例代碼點擊這裏;