[WebGL入門]十八,利用索引緩衝來繪圖

來源:互聯網
上載者:User

標籤:webgl

註:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。



本次的demo的運行結果


對應複雜的模型

上次,通過操作模型座標變換矩陣,實現了多個模型的移動,旋轉和放大縮小。但是,渲染的依然是簡單的三角形,是個構造及其簡單的模型。
但是實際中,用WebGL來繪製一個簡單的三角形的機會是很少見的。至少是個四角多邊形吧,通常會是更複雜的模型。
伴隨著模型的複雜化,頂點的個數也會大幅度增加,越是精密圓滑的模型,所需要的頂點的量也會變的肥大化。javascript中倒是沒有什麼限制,也不是為了節省記憶體或者是節省代碼量,直接繪製這些大量頂點資料也不是說不可以,但是儘可能的減少資料量這種想法是程式員本性。
在WebGL中,有一種方法可以防止頂點資料的肥大化,是使用索引緩衝來管理頂點資料。索引緩衝又叫IBO(index buffer objact),是緩衝的一種。而且,現在為止一直使用的類似的緩衝還有VBO,IBO一般和VBO一起使用。


頂點的增加像剛才說的一樣,頂點增加的越多,管理的頂點資料就越龐大。但是,可以使用反覆使用少量的頂點來形成多邊形。
來看一下具體的例子吧。
多邊形是由三角形組成的,比如一個矩形就是由兩個三角形組成的。

那麼,這時候需要幾個頂點呢?
看剛才的圖的話,需要四個頂點,而定義一個三角形需要三個頂點,兩個多邊形的話,按說是應該需要六個頂點的吧。
頂點的座標情報是完全相同的,如果重複的頂點多次被定義的話,明顯是浪費的。所以,應該考慮頂點的重複利用。

重複的頂點的再利用,儘可能減少頂點資料,這時候頂點緩衝就出現了。頂點緩衝,是用來儲存頂點的索引情報的。像剛才的圖一樣,第一個多邊形使用的是[ 1, 2, 3 ]這三個頂點,同樣,第二個多邊形使用的是[ 2, 3, 4 ]這三個頂點。
這樣,繪製多邊形的時候告訴WebGL使用哪些頂點的就是頂點索引,這次的四邊形效果還不夠明顯,而越是巨大的複雜的模型,使用索引緩衝的作用就會越大。
使用索引緩衝,除了節省代碼量之外,還有另外很大的好處。
使用索引緩衝的話,可以直接使用GPU的記憶體,不用每次繪圖都向GPU傳遞資料,這樣速度會更快。
技能節省代碼量,又實現高速化,所以沒有理由不使用IBO。


IBO的產生索引緩衝的產生步驟,和頂點緩衝類似,雖然有幾個參數變化,但是基本上是同樣的流程。
這次的demo新定義了一個create_ibo函數,來看一下代碼吧。
>自訂函數create_ibo

// IBO的產生函數function create_ibo(data){    // 產生緩衝對象    var ibo = gl.createBuffer();        // 綁定緩衝    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);        // 向緩衝中寫入資料    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW);        // 將緩衝的綁定無效化    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);        // 返回產生的IBO    return ibo;}

和產生VBO的時候一樣,首先使用createBuffer函數來產生空的緩衝對象,然後綁定緩衝,bindBuffer函數的第一個參數是一個和VBO不一樣的內建常量 gl.ELEMENT_ARRAY_BUFFER,指定這個常量產生的緩衝就是IBO。
然後綁定緩衝並寫入資料,VBO的時候,傳入的參數的是Float32Array型資料,但是索引緩衝使用的不是浮點型小數,而是整數,所以這裡使用的是Int16Array型資料。
這個自訂函數create_ibo,傳入的參數是一個數組,和產生VBO的函數create_vbo是一樣的。IBO所用的數組資料和VBO一樣,都是提前定義好的。
>索引資料的數組定義的例子

var index = [    0, 1, 2,    1, 2, 3];

有四個頂點的時候,像上面寫的一樣,第一個三角形使用頂點[ 0, 1, 2 ],第二個三角形使用頂點[ 1, 2, 3 ]來繪製的意思。頂點的索引是從0開始的,需要注意,數組是一維數組。


繪圖相關的變更點瞭解了索引緩衝的產生,接著看一下索引緩衝的使用步驟。
使用IBO進行渲染,需要提前將IBO和WebGL進行綁定,用下面的代碼可以實現。
>IBO綁定的代碼

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
傳入剛才產生的IBO(變數ibo),這樣就完成了IBO的綁定,但是光是綁定IBO,是沒辦法進行繪圖的,還需要修改一下繪圖命令。
到現在為止,渲染多邊形一直是使用drawArrays函數,為了使用IBO進行繪圖,需要使用drawElements函數。這個函數的使用例子如下。
>使用drawElements函數的繪圖命令舉例
gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);
這個drawElements函數有四個參數,第一個參數是表示渲染怎樣的頂點的常量,第二個參數是索引緩衝的元素數,第三個參數是索引資料的資料大小,第四個參數是索引資料的起始索引offset。
比較難理解的是第二個參數,這裡的話,傳入儲存索引資料的數組長度的話就可以了,其他的參數沒什麼特殊限制的話,不做修改就這麼使用的話就行了。


總結使用索引緩衝IBO,既可以節約資料,又能夠提高渲染頂點的效率,而且,模型越複雜影響越大,這是程式員非常樂意看到的。
索引緩衝和頂點緩衝一樣,可以使用純粹的一維數組來產生,產生的IBO進行綁定後就可以使用了,利用IBO繪圖的時候使用drawElements函數是重點。
這次的demo,頂點著色器和片段著色器是沒有任何變化的,所以,還使用以前的HTML代碼,javascript的內容多少有些變化,最後會給出完整代碼,另外,文章最後會貼出本次demo的連結,可以參考一下。


下次,會介紹一下遮擋剔除和深度。


>script.js的完整代碼

onload = function(){    // canvas對象擷取    var c = document.getElementById(‘canvas‘);    c.width = 500;    c.height = 300;        // webgl的context擷取    var gl = c.getContext(‘webgl‘) || c.getContext(‘experimental-webgl‘);        // 頂點著色器和片段著色器的產生     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 position = [         0.0,  1.0,  0.0,         1.0,  0.0,  0.0,        -1.0,  0.0,  0.0,         0.0, -1.0,  0.0    ];// 儲存頂點的顏色情報的數組      var 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,        1.0, 1.0, 1.0, 1.0    ];        // 儲存頂點的索引的數組    var index = [        0, 1, 2,        1, 2, 3    ];        // 產生VBO    var pos_vbo = create_vbo(position);    var col_vbo = create_vbo(color);        // 將VBO進行綁定並添加    set_attribute([pos_vbo, col_vbo], attLocation, attStride);        // 產生IBO    var ibo = create_ibo(index);        // IBO進行綁定並添加    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);        // 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, 5.0], [0, 0, 0], [0, 1, 0], vMatrix);    m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);    m.multiply(pMatrix, vMatrix, tmpMatrix);        // 定義計數器    var count = 0;        // 持續迴圈    (function(){        // canvasを初期化        gl.clearColor(0.0, 0.0, 0.0, 1.0);        gl.clearDepth(1.0);        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);                // 計數器遞增        count++;                // 使用計數器算出角度         var rad = (count % 360) * Math.PI / 180;                // 模型座標變換矩陣的產生(沿著Y軸旋轉)        m.identity(mMatrix);        m.rotate(mMatrix, rad, [0, 1, 0], mMatrix);        m.multiply(tmpMatrix, mMatrix, mvpMatrix);        gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);                // 使用索引進行繪圖        gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);                // context重新整理        gl.flush();                // 為了迴圈,進行遞迴處理          setTimeout(arguments.callee, 1000 / 30);    })();        // 產生著色器的函數      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);      }  }      // IBO的產生函數function create_ibo(data){    // 產生緩衝對象    var ibo = gl.createBuffer();        // 綁定緩衝    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);        // 向緩衝中寫入資料    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW);        // 將緩衝的綁定無效化    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);        // 返回產生的IBO    return ibo;}};

利用索引緩衝(IBO)繪圖的demo

http://wgld.org/s/sample_006/


轉載請註明:轉自lufy_legend的部落格http://blog.csdn.net/lufy_legend

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.