注意:文章翻譯http://wgld.org/。原做者杉本雅広(doxas),文章中假設有個人額外說明,我會加上[lufy:],另外。鄙人webgl研究還不夠深刻。一些專業詞語。假設翻譯有誤。歡迎你們指正。javascript
本次的demo的執行結果
java
上一篇文章中,向多邊形的三個頂點中加入了顏色這個新的頂點屬性,給多邊形塗抹了顏色。而且知道。使用新的VBO可以給對頂點屬性進行自由的擴張。web
那麼。此次來挑戰一下同一時候繪製多個模型。但是,不許備新的VBO。還利用上次的VBO。就是說,反覆利用VBO,僅僅操做座標變換矩陣,來繪製多個模型。
因此,此次着色器相關的內容全然不變。全然使用上次的頂點着色器和片斷着色器,javascript代碼變動的地方也很少,下面重點說一下變動的部分。canvas
此次繪製多個模型。座標變換矩陣相關也是反覆利用。數組
也就是說。在實際中。將多個模型繪製到不一樣的位置的時候,必需要操做的座標變換矩陣僅僅有模型變換矩陣,而決定鏡頭的位置的視圖變換矩陣和決定屏幕範圍的投影變換矩陣這兩個座標變換矩陣,都可以使用同一個。
過程例如如下,依照下面這樣操做矩陣的話就可以了。
・準備好視圖和投影兩個座標變換矩陣
・提早將兩個矩陣相乘並保存起來(下面、pv)
・準備第一個模型座標變換矩陣(下面、m1)
・m1和pv相乘,傳給uniform
・繪製第一個模型
・準備第二個模型座標變換矩陣(下面、m2)
・m2和pv相乘,傳給uniform
・繪製第二個模型
・刷新context
主要是。視圖座標變換矩陣和投影座標變換矩陣提早相乘並保存起來。而後等模型座標變換矩陣準備好以後。再與以前的結果相乘,傳給uniform,而後畫圖。
那麼。來看一下實際的代碼吧。瀏覽器
>script.js中的部分代碼緩存
//各類矩陣的生成和初始化 var mMatrix = m.identity(m.create()); var vMatrix = m.identity(m.create()); var pMatrix = m.identity(m.create()); var tmpMatrix = m.identity(m.create()); var mvpMatrix = m.identity(m.create()); //視圖x投影座標變換矩陣 m.lookAt([0.0, 0.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, tmpMatrix); //移動第一個模型的模型座標變換矩陣 m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix); //模型x視圖x投影(第一個模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //將座標變換矩陣傳入uniformLocation,並畫圖(第一個模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //移動第二個模型的模型座標變換矩陣 m.identity(mMatrix); m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix); //模型x視圖x投影(第二個模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //將座標變換矩陣傳入uniformLocation,並畫圖(第二個模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //刷新context gl.flush();重點是,爲了臨時保存視圖和投影座標變換矩陣,新聲明瞭一個tmpMatrix這種用法。使用matIV.multiply將視圖座標變換矩陣和投影座標變換矩陣相乘,而後結果保存到tmpMatrix中。
而後,使用matIV.translate來操做模型座標變換矩陣,將第一個模型向X方向移動1.5,而後將這個模型座標變換矩陣和tmpMatrix相乘。傳給uniform並進行畫圖。
第二個模型的座標矩陣,和以前相反,向X方向上移動-1.5,而後和第一個同樣,和tmpMatrix相乘。傳給uniform並進行畫圖。
最後,刷新context,在canvas上繪製兩個多邊形。ide
這樣。利用VBO和一部分座標變換矩陣。省去了沒必要要的代碼,繪製了兩個多邊形。函數
需要注意的是。對第二個模型準備模型座標變換矩陣的時候,首先在最初的時候使用matIV.identity對矩陣進行初始化。webgl
假設,不進行初始化直接使用matIV.translate的話。會受前一回移動的影響,致使結果改變。
進行完初始化以後再進行操做,是爲了不類似這種發生錯誤。
上次的文章中也涉及到了,添加頂點屬性的時候,假設將對應的處理函數化的話可以提升效率,儘管和此次的主題(繪製多個模型)沒有關係,但是因爲demo中進行了函數化。因此簡單的講解一下。
>VBO的綁定相關
// 綁定VBO相關的函數 function set_attribute(vbo, attL, attS){ // 處理從參數中獲得的數組 for(var i in vbo){ // 綁定緩存 gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]); // 將attributeLocation設置爲有效 gl.enableVertexAttribArray(attL[i]); //通知並加入attributeLocation gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0); } }這個函數有三個參數。三個參數都是數組。使用for ~ in來循環VBO中的屬性,並進行綁定和加入。
通常,需要準備很是多個VBO,因此將這裏函數化,之後可以省略很是多代碼了吧。
此次操做的是模型座標變換矩陣,介紹了反覆利用VBO。視圖和投影座標變換矩陣。進行多個模型的繪製的方法。
繪製很是多個簡單的模型,圖形的時候,像此次的作法同樣。可以使處理變的簡潔一些。避免寫很是多多餘的代碼。
此次的demo,HTML的代碼和上一次是全然同樣的,就是說。頂點着色器和片斷着色器沒有作不論什麼調整。
javascript代碼有了一些變化,因此貼出全部代碼。另外,在文章的最後面,加入了demo的連接。有支持WebGL的瀏覽器的話。可以直接打開連接看一下效果。
下次,進一步操做模型座標變換矩陣,對繪製的模型進行各類各樣的處理。
>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 = new Array(2); attLocation[0] = gl.getAttribLocation(prg, 'position'); attLocation[1] = gl.getAttribLocation(prg, 'color'); // 將元素數attribute保存到數組中 var attStride = new Array(2); attStride[0] = 3; attStride[1] = 4; // 保存頂點的位置情報的數組 var vertex_position = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0 ]; // 保存頂點的顏色情報的數組 var vertex_color = [ 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0 ]; // 生成VBO var pos_vbo = create_vbo(position); var col_vbo = create_vbo(color); // 將VBO進行綁定並加入 set_attribute([pos_vbo, col_vbo], attLocation, attStride); // 獲取uniformLocation var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix'); // 使用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 tmpMatrix = m.identity(m.create()); var mvpMatrix = m.identity(m.create()); //視圖x投影座標變換矩陣 m.lookAt([0.0, 0.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, tmpMatrix); //移動第一個模型的模型座標變換矩陣 m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix); //模型x視圖x投影(第一個模型) m.multiply(tmpMatrix, mMatrix, mvpMatrix); //將座標變換矩陣傳入uniformLocation,並畫圖(第一個模型) gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); //移動第二個模型的模型座標變換矩陣 m.identity(mMatrix); m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix); //模型x視圖x投影(第二個模型) m.multiply(tmpMatrix, mMatrix, 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; } // 綁定VBO相關的函數 function set_attribute(vbo, attL, attS){ // 處理從參數中獲得的數組 for(var i in vbo){ // 綁定緩存 gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]); // 將attributeLocation設置爲有效 gl.enableVertexAttribArray(attL[i]); //通知並加入attributeLocation gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0); } } };
繪製多個模型的demo