注意:文章翻譯http://wgld.org/。原做者杉本雅広(doxas),文章中假設有個人額外說明,我會加上[lufy:]。另外,鄙人webgl研究還不夠深刻。一些專業詞語,假設翻譯有誤,歡迎你們指正。javascript
這是本次的demo的運行結果java
此次終於該繪製多邊形了,以前的文章(十一,着色器的編譯和鏈接)中介紹了HTML,頂點着色器和片斷着色器,此次看一下javascript從開始到終於的全部處理。web
假設前兩篇文章介紹的內容全然理解的話,此次的內容也應該不難了。也許會有不easy理解的地方,不要着急。
首先,我先貼出全部代碼,而後在慢慢說明。
canvas
>script.js的全部代碼
數組
onload = function(){ // canvas對象獲取 var c = document.getElementById('canvas'); c.width = 300; c.height = 300; // webgl的context獲取 var gl = c.getContext('webgl') || c.getContext('experimental-webgl'); // 設定canvas初始化的顏色 gl.clearColor(0.0, 0.0, 0.0, 1.0); // 設定canvas初始化時候的深度 gl.clearDepth(1.0); // canvas的初始化 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 頂點着色器和片斷着色器的生成 var v_shader = create_shader('vs'); var f_shader = create_shader('fs'); // 程序對象的生成和鏈接 var prg = create_program(v_shader, f_shader); // attributeLocation的獲取 var attLocation = gl.getAttribLocation(prg, 'position'); // attribute的元素數量(此次僅僅使用 xyz ,因此是3) var attStride = 3; // 模型(頂點)數據 var vertex_position = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; // 生成VBO var vbo = create_vbo(vertex_position); // 綁定VBO gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 設定attribute屬性有効 gl.enableVertexAttribArray(attLocation); // 加入attribute屬性 gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0); // 使用minMatrix.js對矩陣的相關處理 // matIV對象生成 var m = new matIV(); // 各類矩陣的生成和初始化 var mMatrix = m.identity(m.create()); var vMatrix = m.identity(m.create()); var pMatrix = m.identity(m.create()); var mvpMatrix = m.identity(m.create()); // 視圖變換座標矩陣 m.lookAt([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix); // 投影座標變換矩陣 m.perspective(90, c.width / c.height, 0.1, 100, pMatrix); // 各矩陣想成。獲得終於的座標變換矩陣 m.multiply(pMatrix, vMatrix, mvpMatrix); m.multiply(mvpMatrix, mMatrix, mvpMatrix); // uniformLocation的獲取 var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix'); // 向uniformLocation中傳入座標變換矩陣 gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); // 繪製模型 gl.drawArrays(gl.TRIANGLES, 0, 3); // context的刷新 gl.flush(); // 生成着色器的函數 function create_shader(id){ // 用來保存着色器的變量 var shader; // 依據id從HTML中獲取指定的script標籤 var scriptElement = document.getElementById(id); // 假設指定的script標籤不存在,則返回 if(!scriptElement){return;} // 推斷script標籤的type屬性 switch(scriptElement.type){ // 頂點着色器的時候 case 'x-shader/x-vertex': shader = gl.createShader(gl.VERTEX_SHADER); break; // 片斷着色器的時候 case 'x-shader/x-fragment': shader = gl.createShader(gl.FRAGMENT_SHADER); break; default : return; } // 將標籤中的代碼分配給生成的着色器 gl.shaderSource(shader, scriptElement.text); // 編譯着色器 gl.compileShader(shader); // 推斷一下着色器是否編譯成功 if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){ // 編譯成功,則返回着色器 return shader; }else{ // 編譯失敗,彈出錯誤消息 alert(gl.getShaderInfoLog(shader)); } } // 程序對象的生成和着色器鏈接的函數 function create_program(vs, fs){ // 程序對象的生成 var program = gl.createProgram(); // 向程序對象裏分配着色器 gl.attachShader(program, vs); gl.attachShader(program, fs); // 將着色器鏈接 gl.linkProgram(program); // 推斷着色器的鏈接是否成功 if(gl.getProgramParameter(program, gl.LINK_STATUS)){ // 成功的話,將程序對象設置爲有效 gl.useProgram(program); // 返回程序對象 return program; }else{ // 假設失敗,彈出錯誤信息 alert(gl.getProgramInfoLog(program)); } } // 生成VBO的函數 function create_vbo(data){ // 生成緩存對象 var vbo = gl.createBuffer(); // 綁定緩存 gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 向緩存中寫入數據 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); // 將綁定的緩存設爲無效 gl.bindBuffer(gl.ARRAY_BUFFER, null); // 返回生成的VBO return vbo; } };
那麼,從上到下按順序來看一下吧。
首先,前提是在頁面載入的同一時候運行script.js中的全部代碼。因此。將代碼全都寫進了onload函數中。瀏覽器
以後,獲取canvas對象開始處理。緩存
>canvas的獲取和初始化例如如下
ide
// canvas對象獲取 var c = document.getElementById('canvas'); c.width = 300; c.height = 300; // webgl的context獲取 var gl = c.getContext('webgl') || c.getContext('experimental-webgl'); // 設定canvas初始化的顏色 gl.clearColor(0.0, 0.0, 0.0, 1.0); // 設定canvas初始化時候的深度 gl.clearDepth(1.0); // canvas的初始化 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);最早開始作的是獲取canvas對象,並設定canvas的大小爲寬300px,高300px。而後獲取WebGL的context,以及設定清除畫面所使用的顏色。
相同。clear函數中的參數也作了對應的變化,不光是顏色,還包括深度。因此添加了gl.DEPTH_BUFFER_BIT常量。
函數
生成頂點着色器和片斷着色器。並使用程序對象進行鏈接。在曾經的文章(着色器的編譯和鏈接)中進行過具體的說明。可以參考一下。webgl
>着色器和程序對象相關的處理
// 頂點着色器和片斷着色器的生成 var v_shader = create_shader('vs'); var f_shader = create_shader('fs'); // 程序對象的生成和鏈接 var prg = create_program(v_shader, f_shader); // attributeLocation的獲取 var attLocation = gl.getAttribLocation(prg, 'position'); // attribute的元素數量(此次僅僅使用 xyz ,因此是3) var attStride = 3;注意這裏出現的函數。並不是WebGL中內置的函數,而是本身寫的。具體點就是說create_shader和create_program都是本身寫的函數。
create_shader函數的參數是HTML中的id字符串,從script標籤中獲取到着色器代碼,生成着色器對象並返回。
上面的樣例中。是依據id是vs和fs的這兩個script標籤中的內容。來生成頂點着色器和片斷着色器。
着色器生成後,做爲參數傳給create_program函數,返回值就是程序對象,而後用變量保存起來。在create_program函數中,生成程序對象,並將着色器進行鏈接。
接着。定義了兩個變量,attLocation和attStride,用來保存在後面向頂點着色器中傳入數據時的必要的內容。
話說回來,看一下曾經的文章中的代碼中是怎樣使用生成的着色器的。而後就明確爲何要定義這兩個變量了吧。
>使用頂點着色器的代碼
attribute vec3 position; uniform mat4 mvpMatrix; void main(void){ gl_Position = mvpMatrix * vec4(position, 1.0); }此次使用着色器的時候。僅僅使用了一個attribute變量,固然就是position了,這個position變量定義成了vec3。說明是擁有3各元素的向量。
這裏的重點就是[利用一個叫作position的attribute變量]和[這個變量是vec3類型]這兩點內容。
事實上利用attribute變量向着色器中傳遞數據的時候,必要的兩個情報是這個數據是第幾個attribute變量,以及這個變量由幾個元素組成的。
也就是說。變量attLocation是爲了保存這個數據是第幾個,變量attStride是爲了保存這個數據是由幾個元素組成的。
WebGL的context中的getAttribLocation函數的兩個參數,第一個參數是程序對象。第二個參數是想要獲取的attribute變量的變量名。返回值是數值型,就是向頂點着色器傳遞數據時的序號。就是告訴咱們是第幾個變量。這個數值後面會使用到。
變量attStride表示頂點着色器中的attribute變量position是一個有三個元素的vec3類型的變量。這個後面也會用到。
繼續吧,接着是定義模型數據。並生成VBO,而後爲了將VBO傳給頂點着色器,進行綁定並傳入數據。
>VBO生成的相關處理
// 模型(頂點)數據 var vertex_position = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; // 生成VBO var vbo = create_vbo(vertex_position); // 綁定VBO gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 設定attribute屬性有効 gl.enableVertexAttribArray(attLocation); // 加入attribute屬性 gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);曾經的文章( 十二。模型數據和頂點屬性)中也有過具體說明。將頂點數據定義爲簡單的數組,而後依據這個數組數據。用本身定義函數create_vbo來生成頂點緩存(VBO)。
爲了將頂點緩存和頂點着色器中的attribute變量聯繫起來,首先要向WebGL中綁定VBO。而後使用剛纔獲取到的attribute屬性的序號。將這個attribute屬性設定爲有效。使用WebGL的函數enableVertexAttribArray可以讓指定屬性變爲有效。
接着。使用WebGL函數vertexAttribPointer向着色器中寫入數據。
剛纔定義的兩個變量attLocation和attStride。在這裏也用到了。
vertexAttribPointer函數的第一個參數是attribute變量的序號,第二個參數是元素數。第三個參數是指定了數據類型的內置常量。gl.FLOAT是一個表示浮點型的常量。第四~第六各參數基本上是不怎麼變的,依據內存有時候會傳入其它的內容。
需要注意的是,運行vertexAttribPointer的時候。VBO對象必須先進行綁定。哪一個VBO以及和它關聯的attribute屬性是必須的,因此不要忘了先將VBO跟WebGL進行綁定。
接着,是準備渲染用的座標變換矩陣。這裏使用了本站點製做的矩陣計算的庫minMatrix.js。minMatrix.js的主要的用法在上一篇文章中(minMatrix.js和座標變換矩陣)已經介紹過了。
>座標變換矩陣的生成以及相關的處理
// 使用minMatrix.js對矩陣的相關處理 // matIV對象生成 var m = new matIV(); // 各類矩陣的生成和初始化 var mMatrix = m.identity(m.create()); var vMatrix = m.identity(m.create()); var pMatrix = m.identity(m.create()); var mvpMatrix = m.identity(m.create()); // 視圖變換座標矩陣 m.lookAt([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix); // 投影座標變換矩陣 m.perspective(90, c.width / c.height, 0.1, 100, pMatrix); // 各矩陣想成,獲得終於的座標變換矩陣 m.multiply(pMatrix, vMatrix, mvpMatrix); m.multiply(mvpMatrix, mMatrix, mvpMatrix); // uniformLocation的獲取 var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix'); // 向uniformLocation中傳入座標變換矩陣 gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);此次,模型變換矩陣初始化以後沒有作不論什麼處理就直接使用了,固然,並不是說不能操做模型變換矩陣,現在就先這麼使用了。
>>uniform系列函數相關
此次出現的uniformMatrix4fv僅僅是uniform系列函數的一個。uniform系列函數有很是多種,主要分爲下面幾大類。
uniform 系列
uniform系列有uniform1 ~ uniform4,分別在向頂點着色器中傳入一個到四各元素時使用。依據傳給頂點着色器的數據的類型爲整型或者浮點型,數字的後面會加上i(int)或者f(float)等小寫字母。比方。傳給着色器的數據是有兩個元素的浮點型數據的時候,使用uniform2f。
演示樣例 : gl.uniform2f(uniformLocation, date1, data2);
uniform v 系列
這一系列。基本上和上面的uniform系列沒有太大區別,傳入的數據是數組的時候使用。
和uniform系列同樣,有1 ~ 四、數據類型也是在後面加入i或者f。
演示樣例 : gl.uniform3iv(uniformLocation, Array);
uniformMatrix 系列
看名字就應該知道了。這個系列是在矩陣的時候使用。
固然,矩陣不可能出現1,因此僅僅有2 ~ 4。而且,矩陣中基本上僅僅使用浮點型小數、因此,數據類型也不存在i。數據就是以使用數組爲前提的。因此後面加不加v都是沒有區別的。
演示樣例 : gl.uniformMatrix4fv(uniformLocation, false, Matrix);
着色器,頂點數據,座標變換矩陣等,各類準備工做都完工了,終於該寫繪製命令了。
>繪製命令和刷新
// 繪製模型 gl.drawArrays(gl.TRIANGLES, 0, 3); // context的刷新 gl.flush();運行WebGL的drawArrays函數的話,模型就被繪製到了緩存中了,這裏之因此說是[緩存中],是因爲當運行drawArrays函數的時候,尚未把多邊形繪製到畫面上。
這裏出現的drawArrays函數,第一個參數是指定怎樣使用頂點進行畫圖的一個常量,第二個參數是從第幾個頂點開始使用,第三個參數是繪製幾個頂點。
這一次,使用的是gl.TRAIANGLES,因此頂點被當成了純粹的三角形多邊形。利用三個頂點進行了繪製。
此次的文章有點太長了,貼出的代碼量有點多。可能有人會吃了一驚吧。
事實上此次的代碼繪製的僅僅是一個簡單的三角形。僅僅是這樣。卻寫了這麼長的代碼,因此才說3D開發是比較難的。
但是。我的以爲,即便這樣。和DirectX相比較的話,已經至關簡單,簡練了。
單從開發環境上來講。不需要特別的開發環境,WebGL這一點已經很是輕鬆了吧。理解了本次的內容的話,僅僅需要略微慢慢的調整一下。就可以實現很是多效果。這之後的內容,都是以此次的內容爲基礎的,因此必需要好好的理解一下。
本文章最後,給出本文demo的連接,假設瀏覽器支持的話。直接看demo比較直觀吧。
那麼。下次給多邊形進行着色,敬請期待。
渲染三角形的demo