WebGL第一步

什麼是WebGL?

WebGL使用了GLSL ES(OpenGL ES)着色器語言,經過配合調用js相關的繪製接口來實現3D效果。web

採用頁面中的<canvas>元素來定義繪圖區域,canvas支持三維圖形的繪製,但它不直接提供繪圖方法,而是提供一種叫上下文(context)機制來繪製圖形。canvas

繪製流程

獲取畫布canvas和3D繪製上下文

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 = "...";

編譯着色器並提交到GPU

1.建立Shader對象

createShader方法,須要傳遞參數指明是頂點着色器仍是片斷着色器;緩存

2.將寫好的着色器附加到Shader上

shaderSource方法,能夠指定着色器代碼添加到指定的Shader上;webgl

3.編譯着色器

compileShader方法,能夠將附加了着色器的shader對象進行編譯;spa

4.建立程序對象

createProgram方法,能夠建立一個程序對象,一個可用的WebGLProgram對象由兩個編譯事後的WebGLShader組成,即頂點着色器和片斷着色器;code

5.添加着色器

attachShader方法,能夠把一個編譯好的着色器對象添加到一個程序對象上;orm

6.鏈接程序對象

linkProgram方法,鏈接一個程序對象,鏈接以後的程序對象可使用;對象

7.使用程序對象

useProgram方法,設定一個程序對象爲當前使用的程序對象;blog

8.調用繪製方法繼續繪製

根據須要調用對應的方法進行繪製;

示例代碼點擊這裏;

繪製三角形

繪製圖像須要向GPU傳遞多個頂點,WebGL提供了一種很方便的機制,緩衝區對象(buffer object),它是WebGL系統中的一塊內存區域,它能夠一次性向着色器傳入多個頂點的數據,而後將這些數據保存在其中,供頂點着色器使用。

繪製圖像除了要完成上面說的步驟以外,還有下面的步驟須要完成:

1.使用緩衝區對象向GPU傳入多個頂點數據

建立緩衝區對象

createBuffer方法,建立緩衝區對象;

將緩衝區對象綁定到目標

bindBuffer方法,綁定緩衝區對象,同時代表緩衝區的類型,以下:

  • gl.ARRAY_BUFFER: 包含頂點屬性的Buffer,如頂點座標,紋理座標數據或頂點顏色數據。
  • gl.ELEMENT_ARRAY_BUFFER: 用於元素索引的Buffer。

向緩衝區寫入數據

bufferData方法,須要代表緩衝數據的類型,同上,還要代表數據是否會變更,以下:

  • gl.STATIC_DRAW: 緩衝區的內容可能常用,而不會常常更改。內容被寫入緩衝區,但不被讀取。
  • gl.DYNAMIC_DRAW: 緩衝區的內容可能常常被使用,而且常常更改。內容被寫入緩衝區,但不被讀取。
  • gl.STREAM_DRAW: 緩衝區的內容可能不會常用。內容被寫入緩衝區,但不被讀取。

獲取屬性下標位置

getAttribLocation方法,獲取指定屬性的下標;

將緩衝區對象分配給指定的名稱變量

vertexAttribPointer方法,會告知到GPU的屬性怎麼獲取傳入的數據,參數分別以下:

  • 第一個參數:指定待分配attribute變量的存儲位置
  • 第二個參數:指定緩存區中每一個頂點的份量個數(1~4)
  • 第三個參數:類型有,gl.UNSIGNED_BYTE無符號字節,gl.SHORT短整數,gl.UNSIGNED_SHORT無符號短整數,gl.INT整型,gl.UNSIGNED_INT無符號整型,gl.FLOAT浮點型。
  • 第四個參數:表示是否將非浮點型的數據歸到[0,1][-1,1]區間
  • 第五個參數:相鄰兩個頂點的字節數。默認爲0
  • 第六個參數:表示緩存區對象的偏移量(以字節爲單位),就是attribute變量從緩衝區中的何處開始存儲。

開啓attribute變量

enableVertexAttribArray方法,就是開啓指定的變量,使緩存區對attribute變量分配生效,以便頂點着色器可以訪問緩衝區內的數據。

2.着色器獲取外部傳入的數據

使用attribute定義的屬性都是能夠接收外部傳入的數據的屬性,接收到的數據能夠直接在着色器中使用;

1 attribute vec4 a_Position;
2 
3 void main() {
4 gl_Position = a_Position;
5 }

3.繪製圖像

drawArrays方法,用來繪製圖像,參數分別以下:

第一個參數:繪製類型

  • 點 gl.POINTS 一系列點,繪製v0,v1……。
  • 線段 gl.LINES 一系列單獨的線段,繪製(v0,v1),(v2,v3)……若是是奇數,最後一個省略。
  • 線條 gl.LINE_STRIP 一系列鏈接的線段,繪製(v0,v1),(v1,v2),(v2,v3)……除了第一個和最後一個,其餘的點點便是起點又是終點。
  • 迴路 gl.LINE_LOOP 一系列鏈接的線段,繪製(v0,v1),(v1,v2),(v2,v3)……(vn,v0),最後一個點會鏈接起點。

三角形 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.先在代碼中定義好須要參與運算的矩陣:

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 ]);

2.將矩陣數據傳入GPU:

uniformMatrix4fv方法,傳輸到GPU,參數以下:

  • 第一個參數:表明uniform變量的存儲位置;
  • 第二個參數:在WebGL中指定爲false;
  • 第三個參數:待傳輸的類型化數組;

代碼以下:

1 // 獲取矩陣變量的下標
2 var u_xformMatarix = gl.getUniformLocation(shaderProgram, 'u_xformMatarix');
3 // 提交矩陣數據到GPU
4 gl.uniformMatrix4fv(u_xformMatarix, false, xformMatrix);

3.在着色器中獲取矩陣並進行運算

1 attribute vec4 a_Position;
2 uniform mat4 u_xformMatarix;
3 
4 void main() {
5 // 設置座標
6 gl_Position = u_xformMatarix * a_Position;
7 }

示例代碼點擊這裏;

設定顏色

咱們能夠經過頂點數據向着色器添加顏色信息。

1.爲頂點數據添加顏色的信息

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 ]);

2.編寫着色器

顏色是在片斷着色器中處理的,可是頂點數據只在頂點着色器中可使用,因此咱們須要將頂點着色器中的部分數據傳遞到片斷着色器中,使用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 }

3.設置傳遞的數據規則

 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來繪製圖片;

1.加載圖片

經過建立Image對象,能夠動態加載指定的圖片,保存這個Image對象,後期直接使用這個對象提交到GPU便可;

2.提交到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);

3.UV座標

Webgl中,是使用UV座標來標記圖像的採樣點,UV的詳情點擊這裏;

4.着色器修改

 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和drawElements

咱們以前是提交頂點數據,而後經過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.定義索引數組

1 //定義索引數組
2 var indexDatas =
3 [
4 0, 1, 2,
5 2, 1, 3,
6 ];

2.提交到GPU

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);

3.着色器代碼

不須要作任何的修改;

4.提交繪製

gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

  • 第一個參數:繪製模式,可選:gl.LINE_LOOP、gl.LINES、gl.TRIANGLES等
  • 第二個參數:繪製頂點個數
  • 第三個參數:索引數據類型,gl.UNSIGNED_BYTE對應Uint8Array,gl.UNSIGNED_SHORT對應Uint16Array
  • 第四個參數:從第幾個點開始繪製

示例代碼點擊這裏;

相關文章
相關標籤/搜索