[WebGL入門]十七,遞迴處理和移動・旋轉・縮放,webgl遞迴
註:文章譯自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的額外說明,我會加上[lufy:],另外,鄙人webgl研究還不夠深入,一些專業詞語,如果翻譯有誤,歡迎大家指正。
本次的demo的運行結果
模型座標變換矩陣的好處上次,通過操作模型座標變換矩陣,繪製了多個模型。這次,繼續在此基礎上,給多個模型再添加上旋轉和放大縮小等處理。
看過上一篇文章的人應該知道,在3D渲染的世界裡,利用VBO和一部分座標變換矩陣,只需要少量修改,就可以繪製出大量的模型。當然,計算量也會變少,效率也自然得到了提升。
換句話說,能夠熟練使用模型座標變換矩陣的話,就可以實現高效的渲染,這就是模型座標變換的好處。
這次,試著繪製三個多邊形,一個多邊形沿著一個圓進行移動,一個多邊形沿Y軸進行旋轉,第三個多邊形進行放大縮小。
添加一個持續迴圈的處理上次的demo,只是在canvas上更新了一次,這次的demo會讓模型持續變化,做成一個簡單的動畫效果。
為了進行動畫處理,需要添加一個持續的迴圈,在javascript中持續迴圈有很多種方法,本網站基本上就是使用setTimeout函數。
這裡說的setTimeout函數是javascript中window對象內建的函數,在指定的時間過後,可以進行指定的處理。
經過的時間的單位是毫秒,1秒是1000毫秒,所以需要指定描述的1千倍。
使用setTimeout實現遞迴處理按照剛才說的,使用setTimeout函數可以實現反覆的迴圈處理,那麼具體的做法是怎樣的呢?
setTimeout函數的第一個參數是調用的函數,第二個參數是需要經過多長時間(毫秒)後調用這個函數。如果第一個參數指定為當前所啟動並執行函數的話,那麼就可以實現持續迴圈了。
・函數A被調用
・在函數A中,使用setTimeout,並傳入函數A作為參數
・經過指定的時間後,函數A被調用
按照上面的步驟,把WebGL中繪圖部分寫成遞迴函式,就可以持續迴圈了。
arguments和callee屬性函數的內部調用函數本身的話,可以直接寫函數的名字來實現,但是如果是匿名函數的話,這樣的做法就行不通了。
解決的辦法是有的,使用arguments和callee屬性的話就可以調用函數本身了。arguments對象是函數被調用的時候自動產生的,而callee屬性就是這個函數本身的引用,使用這種方法的話,即使是匿名函數也可以實現遞迴。
這次的遞迴處理,就是使用這裡所說的setTimeout+arguments.callee組合來實現。
遞迴函式中的處理向遞迴函式中,添加最少限度的處理。因為在反覆迴圈的處理中,如果添加了多餘的沒用的處理的話,會導致程式效率降低。
比如,WebGL的context的擷取,VBO和著色器的產生等,是不用每次都調用的。一旦對象被產生的話,就可以反覆利用了,同樣,視圖座標變換矩陣和投影座標變換矩陣的產生也沒有必要放在遞迴函式中,因為每次都是使用相同的東西。
這樣的話,遞迴函式中所包含處理就基本上限定了,具體的就是下面列出的處理。
・清空畫面
・模型座標變換矩陣的產生
・向uniform中傳入座標變換矩陣
・繪圖命令
・畫面重新整理
・setTimeout+arguments.callee
那麼,看一下遞迴函式部分的代碼吧。
>遞迴函式的代碼
// 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; // 模型1按照一個圓形軌道進行旋轉 var x = Math.cos(rad); var y = Math.sin(rad); m.identity(mMatrix); m.translate(mMatrix, [x, y + 1.0, 0.0], mMatrix); // 完成模型1的座標變換矩陣,並進行繪圖 m.multiply(tmpMatrix, mMatrix, mvpMatrix); gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); // 模型2沿Y軸進行旋轉 m.identity(mMatrix); m.translate(mMatrix, [1.0, -1.0, 0.0], mMatrix); m.rotate(mMatrix, rad, [0, 1, 0], mMatrix); // 完成模型2的座標變換矩陣,並進行繪圖 m.multiply(tmpMatrix, mMatrix, mvpMatrix); gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); // 模型3進行放大縮小 var s = Math.sin(rad) + 1.0; m.identity(mMatrix); m.translate(mMatrix, [-1.0, -1.0, 0.0], mMatrix); m.scale(mMatrix, [s, s, 0.0], mMatrix) // 完成模型3的座標變換矩陣,並進行繪圖 m.multiply(tmpMatrix, mMatrix, mvpMatrix); gl.uniformMatrix4fv(uniLocation, false, mvpMatrix); gl.drawArrays(gl.TRIANGLES, 0, 3); // context重新整理 gl.flush(); // 為了迴圈,進行遞迴處理 setTimeout(arguments.callee, 1000 / 30);})();
文章的最後,會給出demo的連結,大家可以直接查看完整的代碼,HTML代碼依然沒有做任何修改。
這次處理的重點是在匿名函數外面聲明了count,匿名函數中讓這個變數每次遞增,然後使用這個變數的值來進行模型座標變換。
變數count對360取餘數,然後用這個餘數來計算弧度。
>擷取弧度的代碼
var rad = (count % 360) * Math.PI / 180;
無論變數count有多大,餘數始終是在0 ~ 359這個範圍內,這樣就能得到正確的角度了,從而得到弧度,然後使用這個弧度作為其中一個參數,來進行模型座標變換。
第一個模型,使用弧度來計算sin和cos,得到X和Y就是移動的量。
第二個模型,為了沿著Y軸旋轉,也使用了這個弧度作為旋轉量。
第三個模型,使用弧度的sin值來計算了放大縮小的量。
注意這裡頻繁出現的multiply和rotate等函數,都是為了實現各種座標變換而封裝在minMatrix.js中函數。參數的指定可以看之前的文章(minMatrix.js和座標變換矩陣),裡面已經做了詳細的解說。
另外,需要注意模型座標變換的順序,移動,旋轉和放大縮小等各種變換,執行的順序如果不對的話,結果就有變化了。這裡之前的文章也都說過了,不清楚的人可以複習一下前面的內容。
總結這次利用遞迴處理實現了持續迴圈,並介紹了模型座標變換矩陣的處理。以後,動態demo會越來越多,這次介紹的持續迴圈也會被更多的用到。
匿名函數或者普通的函數的遞迴可能是個稍微難理解的概念,也要著急,仔細考慮的話會理解的。如果處理比較多的話,這一部分處理可能應該單獨分離出來。
下次,介紹一個緩衝索引。
下面的連結是這次的demo,可以直接運行,所以一定參考一下。
模型的移動,旋轉和放大縮小的動態demo
http://wgld.org/s/sample_005/
轉載請註明: